using System.Net.Http.Json;
using Managing.Infrastructure.Evm.Abstractions;
using Microsoft.Extensions.Options;
using System.Text.Json;
using System.Web;
using Managing.Infrastructure.Evm.Models.Proxy;

namespace Managing.Infrastructure.Evm.Services
{
    public class Web3ProxySettings
    {
        public string BaseUrl { get; set; } = "http://localhost:3000";
    }

    public class Web3ProxyService : IWeb3ProxyService
    {
        private readonly HttpClient _httpClient;
        private readonly Web3ProxySettings _settings;
        private readonly JsonSerializerOptions _jsonOptions;

        public Web3ProxyService(IOptions<Web3ProxySettings> options)
        {
            _httpClient = new HttpClient();
            _settings = options.Value;
            _jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };
        }

        public async Task<T> CallPrivyServiceAsync<T>(string endpoint, object payload)
        {
            if (!endpoint.StartsWith("/"))
            {
                endpoint = $"/{endpoint}";
            }

            var url = $"{_settings.BaseUrl}/api/privy{endpoint}";

            try
            {
                var response = await _httpClient.PostAsJsonAsync(url, payload, _jsonOptions);

                if (!response.IsSuccessStatusCode)
                {
                    await HandleErrorResponse(response);
                }

                return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
            }
            catch (Exception ex) when (!(ex is Web3ProxyException))
            {
                SentrySdk.CaptureException(ex);
                throw new Web3ProxyException($"Failed to call Privy service at {endpoint}: {ex.Message}");
            }
        }

        public async Task<T> GetPrivyServiceAsync<T>(string endpoint, object payload = null)
        {
            if (!endpoint.StartsWith("/"))
            {
                endpoint = $"/{endpoint}";
            }

            var url = $"{_settings.BaseUrl}/api/privy{endpoint}";

            if (payload != null)
            {
                url += BuildQueryString(payload);
            }

            try
            {
                var response = await _httpClient.GetAsync(url);

                if (!response.IsSuccessStatusCode)
                {
                    await HandleErrorResponse(response);
                }

                return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
            }
            catch (Exception ex) when (!(ex is Web3ProxyException))
            {
                SentrySdk.CaptureException(ex);
                throw new Web3ProxyException($"Failed to get Privy service at {endpoint}: {ex.Message}");
            }
        }

        public async Task<T> CallGmxServiceAsync<T>(string endpoint, object payload)
        {
            if (!endpoint.StartsWith("/"))
            {
                endpoint = $"/{endpoint}";
            }

            var url = $"{_settings.BaseUrl}/api/gmx{endpoint}";

            try
            {
                var response = await _httpClient.PostAsJsonAsync(url, payload, _jsonOptions);

                if (!response.IsSuccessStatusCode)
                {
                    await HandleErrorResponse(response);
                }

                return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
            }
            catch (Exception ex) when (!(ex is Web3ProxyException))
            {
                SentrySdk.CaptureException(ex);
                throw new Web3ProxyException($"Failed to call GMX service at {endpoint}: {ex.Message}");
            }
        }

        public async Task<T> GetGmxServiceAsync<T>(string endpoint, object payload = null)
        {
            if (!endpoint.StartsWith("/"))
            {
                endpoint = $"/{endpoint}";
            }

            var url = $"{_settings.BaseUrl}/api/gmx{endpoint}";

            if (payload != null)
            {
                url += BuildQueryString(payload);
            }

            try
            {
                var response = await _httpClient.GetAsync(url);

                if (!response.IsSuccessStatusCode)
                {
                    await HandleErrorResponse(response);
                }

                return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
            }
            catch (Exception ex) when (!(ex is Web3ProxyException))
            {
                SentrySdk.CaptureException(ex);
                throw new Web3ProxyException($"Failed to get GMX service at {endpoint}: {ex.Message}");
            }
        }

        private async Task HandleErrorResponse(HttpResponseMessage response)
        {
            var statusCode = (int)response.StatusCode;

            try
            {
                // Try to parse as the Web3Proxy error format (success: false, error: string)
                var content = await response.Content.ReadAsStringAsync();
                var errorResponse = await response.Content.ReadFromJsonAsync<Web3ProxyResponse>(_jsonOptions);

                if (errorResponse != null && !errorResponse.Success && !string.IsNullOrEmpty(errorResponse.Error))
                {
                    // Handle the standard Web3Proxy error format
                    throw new Web3ProxyException(errorResponse.Error);
                }

                // Fallback for other error formats
                try
                {
                    // Try to parse as structured error if it doesn't match the simple format
                    var structuredErrorResponse =
                        await response.Content.ReadFromJsonAsync<Web3ProxyErrorResponse>(_jsonOptions);

                    if (structuredErrorResponse?.ErrorDetails != null)
                    {
                        structuredErrorResponse.ErrorDetails.StatusCode = statusCode;
                        throw new Web3ProxyException(structuredErrorResponse.ErrorDetails);
                    }
                }
                catch
                {
                    // If we couldn't parse as structured error, use the simple error or fallback
                    throw new Web3ProxyException($"HTTP error {statusCode}: {content}");
                }
            }
            catch (Exception ex) when (!(ex is Web3ProxyException))
            {
                SentrySdk.CaptureException(ex);
                // If we couldn't parse the error as JSON or another issue occurred
                var content = await response.Content.ReadAsStringAsync();
                throw new Web3ProxyException($"HTTP error {statusCode}: {content}");
            }
        }

        private string BuildQueryString(object payload)
        {
            var properties = payload.GetType().GetProperties();
            if (properties.Length == 0)
            {
                return string.Empty;
            }

            var queryString = new System.Text.StringBuilder("?");
            bool isFirst = true;

            foreach (var prop in properties)
            {
                var value = prop.GetValue(payload);
                if (value != null)
                {
                    if (!isFirst)
                    {
                        queryString.Append("&");
                    }

                    var paramName = prop.Name;
                    // Apply camelCase to match JSON property naming
                    paramName = char.ToLowerInvariant(paramName[0]) + paramName.Substring(1);

                    queryString.Append(HttpUtility.UrlEncode(paramName));
                    queryString.Append("=");
                    queryString.Append(HttpUtility.UrlEncode(value.ToString()));

                    isFirst = false;
                }
            }

            return queryString.ToString();
        }
    }
}