using Castle.Core.Logging;

namespace Pilz.Net.Api;

public class ApiClient(string apiUrl) : IApiClient
{
    protected readonly HttpClient httpClient = 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 virtual Task<ApiResponse> SendRequest(string route)
    {
        return SendRequest(route, HttpMethod.Post);
    }

    public virtual Task<ApiResponse> SendRequest(string route, HttpMethod method)
    {
        return SendRequest(route, method, null);
    }

    public virtual Task<ApiResponse> SendRequest(string route, ApiMessage? message)
    {
        return SendRequest(route, HttpMethod.Post, message);
    }

    public virtual Task<ApiResponse> SendRequest(string route, HttpMethod method, ApiMessage? message)
    {
        return SendRequest(route, method, message, null);
    }

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

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

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

        return new(res.StatusCode);
    }

    public virtual Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route) where TResponse : ApiMessage
    {
        return SendRequest<TResponse>(route, HttpMethod.Post);
    }

    public virtual Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, HttpMethod method) where TResponse : ApiMessage
    {
        return SendRequest<TResponse>(route, method, null);
    }

    public virtual Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, ApiMessage? message) where TResponse : ApiMessage
    {
        return SendRequest<TResponse>(route, HttpMethod.Post, message);
    }

    public virtual Task<ApiResponse<TResponse>> SendRequest<TResponse>(string route, HttpMethod method, ApiMessage? message) where TResponse : ApiMessage
    {
        return SendRequest<TResponse>(route, method, message, null);
    }

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

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

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

        return new(res.StatusCode, result);
    }

    protected virtual async Task<HttpResponseMessage> Send(string route, HttpMethod method, ApiMessage? message, IApiMessageSerializer serializer)
    {
        var url = ApiUrl + route;
        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? EncodeAuthKey()
    {
        return AuthKey;
    }
}
