using Apewer.Network;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace Apewer.Web
{

    /// <summary></summary>
    public class ApiClient
    {

        private const int DefaultTimeout = 30000;

        /// <summary></summary>
        public string ApiUrl { get; set; }

        /// <summary></summary>
        public string Random { get; set; }

        /// <summary></summary>
        public string Ticket { get; set; }

        /// <summary></summary>
        public string Session { get; set; }

        /// <summary></summary>
        public string Page { get; set; }

        /// <summary></summary>
        public string UserAgent { get; set; }

        /// <summary></summary>
        public int Timeout { get; set; } = DefaultTimeout;

        /// <summary></summary>
        public static Result<Json> Return(byte[] bytes, Action<Json> succeed = null, Action<string> failed = null)
        {
            var text = TextUtility.FromBytes(bytes);
            var json = Json.From(text);
            return Return(json, succeed, failed);
        }

        /// <summary>解析响应并返回。</summary>
        public static Result<Json> Return(Json json, Action<Json> succeed = null, Action<string> failed = null)
        {
            if (json != null && json.Available)
            {
                var status = json["status"];
                if (status == "ok")
                {
                    var data = json.GetProperty("data") ?? Json.NewObject();
                    succeed?.Invoke(data);
                    return new Result<Json>(data);
                }
                else
                {
                    var message = json["message"];
                    return Return(message, failed);
                }
            }
            return Return("响应为空。", failed);
        }

        /// <summary>返回错误。</summary>
        public static Result<Json> Return(string error, Action<string> failed = null)
        {
            var error2 = error.IsEmpty() ? "未知错误。" : error;
            failed?.Invoke(error2);
            return new Result<Json>(error2);
        }

        /// <summary>发起请求。</summary>
        public Result<Json> Send(string url, byte[] body = null, Action<Json> succeed = null, Action<string> failed = null, int timeout = DefaultTimeout)
        {
            if (url.IsEmpty()) return Return("参数 URL 无效。", failed);

            var client = new HttpClient();
            client.Request.Url = url;
            if (UserAgent.NotEmpty()) client.Request.UserAgent = UserAgent;
            if (body == null || body.Length < 1)
            {
                client.Request.Method = HttpMethod.GET;
            }
            else
            {
                client.Request.Method = HttpMethod.POST;
                client.Request.Data = body;
            }

            var ex = client.Send();
            return ex == null ? Return(client.Response.Data, succeed, failed) : Return(ex.Message);
        }

        private string MergeUrl(string application = null, string function = null, IEnumerable<KeyValuePair<string, string>> args = null)
        {
            var ts = new TextSet(true);
            ts["application"] = application;
            ts["function"] = function;
            if (Random.NotEmpty()) ts["random"] = Random;
            if (Page.NotEmpty()) ts["page"] = Random;
            if (Session.NotEmpty()) ts["session"] = Random;
            if (Ticket.NotEmpty()) ts["ticket"] = Random;

            if (args != null)
            {
                foreach (var i in args)
                {
                    if (i.Key.IsEmpty()) continue;
                    if (i.Key == "application") continue;
                    if (i.Key == "function") continue;

                    if (i.Value.IsEmpty()) continue;

                    ts[i.Key] = i.Value;
                }
            }

            var url = TextUtility.AssureEnds(ApiUrl, "/");
            var query = HttpClient.MergeForm(ts);
            if (query.NotEmpty()) url = url.Append("?", query);

            return url;
        }

        private Json MergePost(string application = null, string function = null, Json data = null)
        {
            var json = Json.NewObject();
            json.SetProperty("application", application);
            json.SetProperty("function", function);
            json.SetProperty("random", Random);
            json.SetProperty("page", Page);
            json.SetProperty("session", Session);
            json.SetProperty("ticket", Ticket);
            json.SetProperty("data", data);
            return json;
        }

        // GET

        /// <summary></summary>
        public Result<Json> Get(string application = null, string function = null, IEnumerable<KeyValuePair<string, string>> args = null, Action<Json> succeed = null, Action<string> failed = null)
        {
            var url = MergeUrl(application, function, args);
            return Send(url, null, succeed, failed, Timeout);
        }

        /// <summary></summary>
        public Result<Json> Get(string application = null, string function = null, TextSet args = null, Action<Json> succeed = null, Action<string> failed = null)
        {
            var url = MergeUrl(application, function, args == null ? null : args.Origin);
            return Send(url, null, succeed, failed, Timeout);
        }

        // POST

        /// <summary></summary>
        public Result<Json> Post(string application = null, string function = null, IEnumerable<KeyValuePair<string, string>> args = null, byte[] body = null, Action<Json> succeed = null, Action<string> failed = null)
        {
            var url = MergeUrl(application, function, args);
            return Send(url, body, succeed, failed, Timeout);
        }

        /// <summary></summary>
        public Result<Json> Post(string application = null, string function = null, TextSet args = null, byte[] body = null, Action<Json> succeed = null, Action<string> failed = null)
        {
            var url = MergeUrl(application, function, args == null ? null : args.Origin);
            return Send(url, body, succeed, failed, Timeout);
        }

        /// <summary></summary>
        public Result<Json> Post(string application = null, string function = null, Json data = null, Action<Json> succeed = null, Action<string> failed = null)
        {
            var json = MergePost(application, function, data);
            var bytes = json.ToString().Bytes();
            return Send(ApiUrl, bytes, succeed, failed, Timeout);
        }

    }

}