using InABox.Core;
using JetBrains.Annotations;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;

namespace InABox.Client.Remote.Json
{
    public class HttpJsonClient
    {
        public string Protocol { get; set; }

        public string URL { get; set; }

        public int Port { get; set; }

        public HttpJsonClient(string protocol, string url, int port)
        {
            Protocol = protocol;
            URL = url;
            Port = port;
        }

        public bool TrySendRequest<T>(RestRequest request, out T result, [NotNullWhen(false)] out string? error)
        {
            var uri = new Uri($"{Protocol}://{URL}:{Port}");
            var cli = new RestClient(uri);

            result = default;
            error = null;
            try
            {
                var res = cli.Execute(request);
                if (res.ErrorException == null)
                {
                    if (res.StatusCode != HttpStatusCode.OK)
                        throw new Exception($"HTTP Request returns {((int)res.StatusCode)} {CoreUtils.SplitCamelCase(res.StatusCode.ToString())}");

                    try
                    {
                        result = Serialization.Deserialize<T>(res.Content, true);
                        return true;
                    }
                    catch (Exception eDeserialize)
                    {
                        error = $"Unable to deserialize response!\n\n{eDeserialize.Message}\n\n{res.Content}";
                    }
                }
                else
                {
                    error = "Could not connect to the server.";
                }
            }
            catch (Exception err)
            {
                error = $"Could not connect to the server: {err.Message}";
            }
            return false;
        }
        public T SendRequest<T>(RestRequest request)
        {
            if (TrySendRequest<T>(request, out var result, out var error))
            {
                return result;
            }
            throw new Exception(error);
        }

        public bool Ping(string endPoint)
        {
            var req = new RestRequest(endPoint, Method.Get)
            {
                Timeout = TimeSpan.FromSeconds(10)
            };

            var uri = new Uri($"{Protocol}://{URL}:{Port}");
            var cli = new RestClient(uri);

            try
            {
                var res = cli.Execute(req);
                if (res.ErrorException == null)
                {
                    return true;
                }
            }
            catch (Exception)
            {
            }
            return false;
        }

        public bool Ping()
        {
            return Ping("");
        }

        public bool TryGetRequest<T>(string command, out T result, [NotNullWhen(false)] out string? error)
        {
            var req = new RestRequest(command, Method.Get)
            {
                Timeout = TimeSpan.FromSeconds(60)
            };
            req.RequestFormat = DataFormat.Json;
            return TrySendRequest<T>(req, out result, out error);
        }
        public T GetRequest<T>(string command)
        {
            if (TryGetRequest<T>(command, out var result, out var error))
            {
                return result;
            }
            throw new Exception(error);
        }

        public T PostRequest<T>(object request, string command)
        {
            var json = Serialization.Serialize(request);

            var req = new RestRequest(command, Method.Post)
            {
                Timeout = TimeSpan.FromSeconds(60)
            };
            req.AddOrUpdateParameter("application/json", json, ParameterType.RequestBody);
            req.RequestFormat = DataFormat.Json;
            return SendRequest<T>(req);
        }

    }
}
