using Castle.Core.Logging;
using System.Web;

namespace Pilz.Net.Api;

public class ApiClient(string apiUrl) : IApiClient
{
    protected readonly List<object> clients = [];

    public HttpClient HttpClient { get; set; } = new();

    public virtual string ApiUrl { get; } = apiUrl;

    public string? AuthKey { get; set; }

    public virtual IApiMessageSerializer Serializer { get; set; } = new DefaultApiMessageSerializer();

    public ILogger Log { get; set; } = NullLogger.Instance;

    public T GetClient<T>() where T : IApiSubClient
    {
        foreach (var c in clients)
        {
            if (c is T t)
                return t;
        }

        if (T.CreateClient<T>(this) is T client)
        {
            clients.Add(client);
            return client;
        }

        throw new ApiException("Could not create an instance of this client.");
    }

    public virtual async Task<ApiResponse> SendRequest(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer? serializer)
    {
        serializer ??= Serializer;

        Log.InfoFormat("Send message to {0}", route);

        var res = await Send(route, method, message, @params, serializer);

        return new(res);
    }

    public virtual async Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer? serializer) where TResponse : ApiMessage
    {
        serializer ??= Serializer;

        Log.InfoFormat("Send request to {0}", route);
        
        var res = await Send(route, method, message, @params, serializer);
        TResponse? result = null;

        if (res.IsSuccessStatusCode)
            result = serializer.Deserialize(await res.Content.ReadAsStringAsync(), typeof(TResponse)) as TResponse;

        return new(res, result);
    }

    protected virtual async Task<HttpResponseMessage> Send(string route, HttpMethod method, ApiMessage? message, ApiParameterCollection? @params, IApiMessageSerializer serializer)
    {
        var url = ApiUrl + route + BuildParameters(@params);
        HttpContent content;

        Log.DebugFormat("Api endpoint url is {0}", url);

        Log.Debug("Create content");

        if (message is not null)
            content = new StringContent(serializer.Serialize(message)!, null, "application/json");
        else
            content = new StringContent(string.Empty, null, "application/json");

        Log.Debug("Build headers");

        content.Headers.Add("API-AUTH-KEY", EncodeAuthKey());

        Log.Debug("Sending request");

        var httpmsg = new HttpRequestMessage(method, url)
        {
            Content = content
        };

        return await HttpClient.SendAsync(httpmsg);
    }

    protected virtual string BuildParameters(ApiParameterCollection? @params)
    {
        if (@params == null || @params.Count == 0)
            return string.Empty;

        return "?" + string.Join("&", @params.Select(kvp =>
        {
            var key = kvp.Key;
            var value = HttpUtility.UrlEncode(ConvertParameter(kvp.Value));
            return $"{key}={value}";
        }));
    }

    protected virtual string? ConvertParameter(object? @param)
    {
        return (string?)Convert.ChangeType(@param, typeof(string));
    }

    protected virtual string? EncodeAuthKey()
    {
        return AuthKey;
    }
}
