using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RudderSdk.Core;
using RudderSdk.Core.Abstractions;
using RudderSdk.Core.Models;

namespace RudderSdk.Unity
{
  internal class UnityTransportAdapter : IRudderTransport
  {
    private readonly string _baseUrl;
    private readonly IUnityRequestExecutor _executor;

    public UnityTransportAdapter(string baseUrl, IUnityRequestExecutor executor)
    {
      _baseUrl = baseUrl.TrimEnd('/');
      _executor = executor;
    }

    public async Task<TResponse> SendAsync<TRequest, TResponse>(
      string method,
      string path,
      TRequest request,
      string accessToken,
      CancellationToken cancellationToken = default)
    {
      var headers = new Dictionary<string, string>
      {
        ["Content-Type"] = "application/json"
      };
      if (!string.IsNullOrEmpty(accessToken))
        headers["Authorization"] = "Bearer " + accessToken;

      var body = request != null ? JsonConvert.SerializeObject(request) : null;

      string responseJson;
      try
      {
        responseJson = await _executor.SendAsync(
          method, _baseUrl + path, body, headers, cancellationToken).ConfigureAwait(false);
      }
      catch (UnityRequestException exception)
      {
        throw MapError(exception);
      }

      if (string.IsNullOrEmpty(responseJson))
        return default;

      return JsonConvert.DeserializeObject<TResponse>(responseJson);
    }

    private static RudderApiException MapError(UnityRequestException exception)
    {
      string code = null;
      string message = null;
      string requestId = null;

      if (!string.IsNullOrEmpty(exception.ResponseBody))
      {
        try
        {
          var error = JsonConvert.DeserializeObject<ErrorResponse>(exception.ResponseBody);
          code = error?.Code;
          message = error?.Error;
          requestId = error?.RequestId;
        }
        catch (JsonException)
        {
        }
      }

      if (string.IsNullOrEmpty(message))
        message = exception.Message;

      switch (exception.StatusCode)
      {
        case 401:
          return new RudderAuthException(exception.StatusCode, code, message, requestId);
        case 404:
          return new RudderNotFoundException(exception.StatusCode, code, message, requestId);
        case 429:
          return new RudderRateLimitException(exception.StatusCode, code, message, requestId);
        default:
          return new RudderApiException(exception.StatusCode, code, message, requestId);
      }
    }
  }
}
