using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using IWS5.Autotest.IWS5.Api.Common.Pashn.Autotests.Pashn.Api.Common;
using IWS5.Autotest.IWS5.Models.Constant;
using RestSharp;
using RestSharp.Serializers.Json;

namespace IWS5.Autotest.IWS5.Api.Common
{
    public abstract class BaseApi
    {
        public const string JsonContentType = ConstantStrings.JsonContentType;
        private const string FormUrlEncodedContentType = ConstantStrings.WebForm;

        private readonly string _serviceUrl;
        private readonly RestClient _client;
        private readonly SessionStorage? _sessionStorage;

        protected BaseApi(string baseUrl, string serviceUrl, SessionStorage? sessionStorage = null)
        {
            _serviceUrl = serviceUrl;
            _client = CreateClient(baseUrl);
            _sessionStorage = sessionStorage;
        }

        protected async Task<RestResponse<TResponse>> ExecuteRequest<TResponse>(RestRequest request)
        {
            var result = await _client.ExecuteAsync<TResponse>(request);
            return result;
        }

        #region JSON Requests

        protected RestRequest CreateGetRequest()
        {
            return CreateRequestWithServiceUrl(null, Method.Get);
        }

        protected RestRequest CreateGetRequest(string url)
        {
            return CreateRequestWithServiceUrl(url, Method.Get);
        }

        protected RestRequest CreatePostRequest<TPayload>(TPayload payload = null, string contentType = JsonContentType)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(null, Method.Post, payload, contentType);
        }

        protected RestRequest CreatePostFormDataRequest(Dictionary<string, string> formData, string url = null)
        {
            return CreateFormDataRequest(url, Method.Post, formData);
        }

        protected RestRequest CreatePostRequest<TPayload>(string url, TPayload payload = null, string contentType = JsonContentType)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(url, Method.Post, payload, contentType);
        }

        protected RestRequest CreatePatchRequest<TPayload>(string url, TPayload payload = null, string contentType = JsonContentType)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(url, Method.Patch, payload, contentType);
        }

        protected RestRequest CreatePutRequest<TPayload>(TPayload payload = null)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(null, Method.Put, payload);
        }

        protected RestRequest CreatePutRequest<TPayload>(string url, TPayload payload = null)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(url, Method.Put, payload);
        }

        protected RestRequest CreatePutRequest(string url)
        {
            return CreateRequestWithServiceUrl(url, Method.Put);
        }

        protected RestRequest CreatePatchRequest(string url)
        {
            return CreateRequestWithServiceUrl(url, Method.Patch);
        }

        protected RestRequest CreatePatchRequest<TPayload>(string url, TPayload payload = null)
            where TPayload : class
        {
            return CreateRequestWithServiceUrl(url, Method.Patch, payload);
        }

        protected RestRequest CreateDeleteRequest()
        {
            return CreateRequestWithServiceUrl(null, Method.Delete);
        }

        protected RestRequest CreateDeleteRequest(string url)
        {
            return CreateRequestWithServiceUrl(url, Method.Delete);
        }

        #endregion

        private RestRequest CreateRequestWithServiceUrl(string url, Method methodType, string contentType = JsonContentType)
        {
            return CreateRequest(CreateServiceUrl(url), methodType, contentType);
        }

        private RestRequest CreateRequestWithServiceUrl<TPayload>(string url, Method methodType, TPayload payload, string contentType = JsonContentType)
            where TPayload : class
        {
            return CreateRequest(CreateServiceUrl(url), methodType, payload, contentType);
        }

        private RestRequest CreateRequest(string url, Method methodType, string contentType = JsonContentType)
        {
            var restRequest = new RestRequest
            {
                Resource = url,
                RequestFormat = DataFormat.Json,
                Method = methodType
            };

            restRequest.AddHeader("Content-type", contentType);

            // Checking if the session storage and token are valid before adding the header
            if (_sessionStorage != null && !string.IsNullOrWhiteSpace(_sessionStorage.token))
            {
                restRequest.AddHeader("Authorization", $"Bearer {_sessionStorage.token}");
            }

            return restRequest;
        }

        private RestRequest CreateRequest<TPayload>(string url, Method methodType, TPayload payload, string contentType = JsonContentType)
            where TPayload : class
        {
            var restRequest = CreateRequest(url, methodType, contentType);

            if (payload != null)
                restRequest.AddJsonBody(payload);

            return restRequest;
        }

        private RestRequest CreateFormDataRequest(string url, Method methodType, Dictionary<string, string> formData = null)
        {
            var restRequest = CreateRequest(CreateServiceUrl(url), methodType, FormUrlEncodedContentType);

            if (formData == null)
            {
                return restRequest;
            }

            foreach (var key in formData.Keys)
            {
                restRequest.AddParameter(key, formData[key]);
            }

            return restRequest;
        }

        private static RestClient CreateClient(string baseUrl)
        {
            var client = new RestClient(
                baseUrl,
                configureSerialization: s => s.UseSystemTextJson(new JsonSerializerOptions
                {
                    Converters = { new JsonStringEnumConverter() },
                    PropertyNameCaseInsensitive = true
                })
            );
            return client;
        }

        private string CreateServiceUrl(string url)
        {
            if (_serviceUrl == null)
            {
                return url;
            }

            return Path.Combine(_serviceUrl, url ?? string.Empty);
        }
    }
}