using Apewer.Network;
using Apewer.Source;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Apewer.Web
{

    /// <summary></summary>
    public static class ApiUtility
    {

        #region Text

        /// <summary>修剪 IP 地址,去除无效的部分。</summary>
        public static string TrimIP(string text)
        {
            var trimmed = TextUtility.Trim(text);
            if (string.IsNullOrEmpty(trimmed)) return "";

            var ip = TextUtility.Trim(trimmed.Split(':')[0]);
            return NetworkUtility.IsIP(ip) ? ip : "";
        }

        /// <summary>按 &amp; 拆分多个参数。</summary>
        public static StringPairs Parameters(string query, bool decode = true)
        {
            var input = query;
            var list = new StringPairs();
            if (!string.IsNullOrEmpty(input))
            {
                if (input[0] == '?') input = input.Substring(1);
                var args = input.Split('&');
                foreach (var arg in args)
                {
                    var equals = arg.IndexOf("=");
                    var left = equals < 0 ? arg : arg.Substring(0, equals);
                    var right = equals < 0 ? "" : arg.Substring(equals + 1);
                    if (decode)
                    {
                        left = TextUtility.DecodeUrl(left);
                        right = TextUtility.DecodeUrl(right);
                    }
                    list.Add(left, right);
                }
            }
            return list;
        }

        /// <summary>获取参数并解码,可要求修剪参数值。</summary>
        public static string Parameter(string encoded, bool trim, params string[] names)
        {
            if (string.IsNullOrEmpty(encoded)) return null;
            if (names == null || names.Length < 1) return null;

            // Names 转为小写,加强适配。
            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                var lower = TextUtility.Lower(name);
                if (string.IsNullOrEmpty(lower)) continue;
                lowerNames.Add(lower);
            }
            if (lowerNames.Count < 1) return null;

            // 分参数对比。
            var parameters = Parameters(encoded);
            var matched = false;
            foreach (var parameter in parameters)
            {
                var left = parameter.Key;
                var right = parameter.Value;
                if (trim) right = TextUtility.Lower(right);
                var lowerLeft = TextUtility.Lower(left);
                if (lowerNames.Contains(right))
                {
                    matched = true;
                    if (!string.IsNullOrEmpty(right)) return right;
                }
            }

            return matched ? "" : null;
        }

        /// <summary>获取参数,可要求修剪参数值。</summary>
        public static string Parameter(StringPairs parameters, bool trim, params string[] names)
        {
            if (parameters == null || parameters.Count < 1) return null;
            if (names == null || names.Length < 1) return null;

            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                var lower = TextUtility.Lower(name);
                if (string.IsNullOrEmpty(lower)) continue;
                lowerNames.Add(lower);
            }

            foreach (var parameter in parameters)
            {
                var lowerKey = TextUtility.Lower(parameter.Key);
                if (lowerNames.Contains(lowerKey))
                {
                    var value = parameter.Value;
                    if (trim) value = TextUtility.Lower(value);
                    if (!string.IsNullOrEmpty(value)) return value;
                }
            }
            return null;
        }

        /// <summary>获取 User Agent。</summary>
        public static string UserAgent(StringPairs headers) => headers == null ? null : headers.GetValue("user-agent");

        // 从 Uri 对象中解析路径片段。
        private static string[] Segmentals(Uri url)
        {
            if (url == null) return null;
            if (string.IsNullOrEmpty(url.AbsolutePath)) return null;
            var segmentals = url.AbsolutePath.Split('/');
            return segmentals;
        }

        // 获取已经解析的路径片段。
        private static string Segmental(string[] segmentals, int index = 3, bool decode = false)
        {
            if (segmentals == null || segmentals.Length < 1) return null;
            if (index < 1 || index >= segmentals.Length) return null;
            var segmental = segmentals[index];
            if (decode) segmental = TextUtility.DecodeUrl(segmental);
            return segmental;
        }

        #endregion

        #region Headers

        /// <summary></summary>
        public static HttpMethod Method(string method)
        {
            if (!string.IsNullOrEmpty(method))
            {
                var upper = TextUtility.Upper(method);
                if (upper.Contains("OPTIONS")) return HttpMethod.OPTIONS;
                else if (upper.Contains("POST")) return HttpMethod.POST;
                else if (upper.Contains("GET")) return HttpMethod.GET;
                else if (upper.Contains("CONNECT")) return HttpMethod.CONNECT;
                else if (upper.Contains("DELETE")) return HttpMethod.DELETE;
                else if (upper.Contains("HEAD")) return HttpMethod.HEAD;
                else if (upper.Contains("PATCH")) return HttpMethod.PATCH;
                else if (upper.Contains("PUT")) return HttpMethod.PUT;
                else if (upper.Contains("TRACE")) return HttpMethod.TRACE;
            }
            return HttpMethod.NULL;
        }

        /// <summary>获取 X-Forwarded-For,不存在时返回 NULL 值。</summary>
        public static string[] GetForwardedIP(StringPairs headers)
        {
            if (headers != null)
            {
                var value = headers.GetValue("x-forwarded-for", true);
                if (!string.IsNullOrEmpty(value))
                {
                    var fips = new List<string>();
                    var split = value.Split(',', ' ');
                    foreach (var para in split)
                    {
                        var fip = TrimIP(para);
                        if (!string.IsNullOrEmpty(fip)) fips.Add(fip);
                    }
                    return fips.ToArray();
                }
            }
            return new string[0];
        }

        #endregion

        #region ApiController

        /// <summary>设置控制器属性。</summary>
        public static void SetProperties(ApiController controller, ApiRequest request, ApiResponse response, ApiOptions options)
        {
            if (controller == null) return;
            controller.Request = request;
            controller.Response = response;
            controller._options = options;
        }

        /// <summary>获取由控制器构造函数指定的初始化程序。</summary>
        public static Func<ApiController, bool> GetInitialier(this ApiController controller) => controller == null ? null : controller._func;

        /// <summary>获取由控制器构造函数指定的默认程序。</summary>
        public static Action<ApiController> GetDefault(this ApiController controller) => controller == null ? null : controller._default;

        /// <summary>获取选项。</summary>
        public static ApiOptions GetOptions(this ApiController controller) => controller == null ? null : controller._options;

        /// <summary>以 POST 转移请求到其它 URL。</summary>
        private static string Transfer(ApiController controller, string url, string application = null, string function = null)
        {
            if (controller == null || controller.Request == null || controller.Response == null) return "ApiControllser 无效。";
            if (url.IsEmpty()) return "ApiController 无效。";

            var s = Json.NewObject();
            s.SetProperty("random", TextUtility.Guid());
            s.SetProperty("application", application.IsEmpty() ? controller.Request.Application : application);
            s.SetProperty("function", function.IsEmpty() ? controller.Request.Function : function);
            s.SetProperty("data", controller.Request.Data);
            s.SetProperty("session", controller.Request.Session);
            s.SetProperty("ticket", controller.Request.Ticket);
            s.SetProperty("page", controller.Request.Page);

            var c = new Network.HttpClient();
            c.Request.Url = url;
            c.Request.Method = Network.HttpMethod.POST;
            // TODO 设置 Request 的 Cookies。
            c.Request.Data = s.ToString().Bytes();

            var e = c.Send();
            if (e != null) return e.Message;

            // TODO 解析 Response 的 Cookies。
            var r = Json.From(c.Response.Data.Text());

            if (r == null || !r.Available)
            {
                controller.Response.Error("请求失败。");
                return "请求失败。";
            }

            if (r["status"] != "ok")
            {
                controller.Response.Error(r["message"]);
                return r["message"];
            }

            controller.Response.Data.Reset(r.GetProperty("data"));
            return null;
        }

        /// <summary>创建指定类型的控制器,并引用当前控制器的 Request 和 Response。</summary>
        public static T Create<T>(this ApiController current) where T : ApiController, new()
        {
            var controller = new T();
            if (current != null)
            {
                controller.Request = current.Request;
                controller.Response = current.Response;
            }
            return controller;
        }

        /// <summary>使用默认控制器处理请求。</summary>
        public static void UseDefault(this ApiController current)
        {
            if (current == null) return;
            var options = GetOptions(current);
            if (options == null) return;
            var type = options.Default;
            if (type == null) return;
            var controller = null as ApiController;
            try
            {
                controller = (ApiController)Activator.CreateInstance(type);
                SetProperties(controller, current.Request, current.Response, options);
            }
            catch
            {
                return;
            }
            controller.GetInitialier()?.Invoke(controller);
        }

        #endregion

        #region ApiRequest

        /// <summary>获取 URL 路径段,不存在的段为 NULL 值。可要求解码。</summary>
        public static string Segmental(this ApiRequest request, int index = 3, bool decode = false)
        {
            if (request == null) return null;
            if (request._segmentals == null) request._segmentals = Segmentals(request.Url);
            return Segmental(request._segmentals, index, decode);
        }

        /// <summary>获取参数,指定可能的参数名,默认将修剪参数值。</summary>
        /// <remarks>当从 URL 中获取参数时将解码。</remarks>
        public static string Parameter(this ApiRequest request, params string[] names) => Parameter(request, true, names);

        /// <summary>获取参数,指定可能的参数名,可要求修剪参数值。</summary>
        /// <remarks>当从 URL 中获取参数时将解码。</remarks>
        public static string Parameter(ApiRequest request, bool trim, params string[] names)
        {
            if (request == null) return null;
            if (names == null || names.Length < 1) return null;

            var dedupNames = new List<string>(names.Length);
            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                if (string.IsNullOrEmpty(name)) continue;
                if (dedupNames.Contains(name)) continue;
                else dedupNames.Add(name);

                var lower = TextUtility.Lower(name);
                if (lowerNames.Contains(lower)) continue;
                else lowerNames.Add(lower);
            }

            var matched = false;

            // POST 优先。
            var data = request.Data;
            if (data != null && data.IsObject)
            {
                var properties = data.GetProperties();
                if (properties != null)
                {
                    // Json 区分大小写,先全字匹配。
                    foreach (var property in properties)
                    {
                        if (!property.IsProperty) continue;
                        var name = property.Name;
                        if (!dedupNames.Contains(name)) continue;
                        var value = property.Value;
                        if (value == null) continue;
                        matched = true;
                        var text = value.ToString();
                        if (trim) text = TextUtility.Trim(text);
                        if (!string.IsNullOrEmpty(text)) return text;
                    }

                    // 以小写模糊匹配。
                    foreach (var property in properties)
                    {
                        if (!property.IsProperty) continue;
                        var name = TextUtility.Lower(property.Name);
                        if (!lowerNames.Contains(name)) continue;
                        var value = property.Value;
                        if (value == null) continue;
                        matched = true;
                        var text = value.ToString();
                        if (trim) text = TextUtility.Trim(text);
                        if (!string.IsNullOrEmpty(text)) return text;
                    }
                }
            }

            // 从已解析的 Get 参数中搜索。
            if (request.Parameters != null)
            {
                var value = Parameter(request.Parameters, trim, names);
                if (!string.IsNullOrEmpty(value)) return value;
                if (value != null) matched = true;
            }

            return matched ? "" : null;
        }

        #endregion

        #region ApiResponse

        internal static ApiModel Model(ApiResponse response, string type, ApiModel model)
        {
            if (response == null && model == null) return null;
            if (!string.IsNullOrEmpty(type)) model.ContentType = type;
            response.Model = model;
            return model;
        }

        /// <summary>设置响应。</summary>
        public static string Respond(ApiResponse response, Json data, bool lower = true)
        {
            if (response == null) return "Response 对象无效。";
            if (data != null)
            {
                if (lower) data = Json.Lower(data);
                response.Data = data;
            }
            return null;
        }

        /// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
        public static string Respond(ApiResponse response, IList list, bool lower = true, int depth = -1, bool force = false)
        {
            if (response == null) return "Response 对象无效。";

            if (list == null)
            {
                var error = "列表对象无效。";
                response.Error(error);
                return error;
            }

            var json = Json.From(list, lower, depth, force);
            if (json == null || !json.Available)
            {
                var error = "列表无法序列化。";
                response.Error(error);
                return error;
            }

            if (response.Data == null) response.Data = Json.NewObject();
            response.Data.SetProperty("count", list.Count);
            response.Data.SetProperty("list", Json.From(list, lower, depth, force));
            return null;
        }

        /// <summary>设置响应,当发生错误时设置响应。返回错误信息。</summary>
        public static string Respond(ApiResponse response, IRecord record, bool lower = true)
        {
            if (response == null) return "Response 对象无效。";

            if (record == null)
            {
                var error = "记录无效。";
                response.Error(error);
                return error;
            }

            var json = Json.From(record, lower);
            if (json == null || !json.Available)
            {
                var error = "记录无法序列化。";
                response.Error(error);
                return error;
            }

            if (response.Data == null) response.Data = Json.NewObject();
            response.Data.Reset(json);
            return null;
        }

        /// <summary>设置 status 为 error,并设置 message 的内容。</summary>
        public static void Error(ApiResponse response, string message = "未知错误。")
        {
            if (response == null) return;
            response.Model = null;
            response.Status = "error";
            response.Message = message ?? TextUtility.Empty;
        }

        /// <summary>设置 status 为 exception,并设置 message 的内容。</summary>
        public static void Exception(ApiResponse response, Exception exception)
        {
            if (response == null) return;
            response.Model = null;
            response.Status = "exception";
            try
            {
                response.Message = exception == null ? "无效异常。" : exception.Message;
                response.Data = ToJson(exception);
            }
            catch { }
        }

        /// <summary></summary>
        private static Json ToJson(Exception exception)
        {
            if (exception == null) return null;
            var json = Json.NewObject();
            try
            {
                var ex = exception;
                json.SetProperty("type", exception.GetType().FullName);
                try
                {
                    json.SetProperty("message", $"ex.Message({ex.GetType().FullName})");
                    json.SetProperty("helplink", ex.HelpLink);
                    json.SetProperty("source", ex.Source);
                    json.SetProperty("stack", Json.From((ex.StackTrace ?? "").Replace("\r", "").Split('\n'), false));
                }
                catch { }

                // WebException 附加数据。
                var webex = ex as System.Net.WebException;
                if (webex != null)
                {
                    json.SetProperty("data", Json.From(webex.Data));
                }
            }
            catch { }
            return json;
        }

        #endregion

        #region ApiModel

        /// <summary>初始化 ApiMode 的属性。</summary>
        public static void Initialize(ApiModel model, ApiRequest request, ApiResponse response, ApiOptions options, ApiProvider provider)
        {
            if (model == null) return;
            model._request = request;
            model._response = response;
            model._options = options;
            model._provider = provider;
        }

        #endregion

        #region Application & Path

        /// <summary>MapPath。</summary>
        public static string MapPath(params string[] names)
        {
            var list = new List<string>();
            if (names != null)
            {
                var invalid = StorageUtility.InvalidPathChars;
                foreach (var name in names)
                {
                    if (string.IsNullOrEmpty(name)) continue;
                    var split = name.Split('/', '\\');
                    foreach (var s in split)
                    {
                        var sb = new StringBuilder();
                        foreach (var c in s)
                        {
                            if (Array.IndexOf(invalid, "c") < 0) sb.Append(c);
                        }
                        var t = sb.ToString();
                        if (!string.IsNullOrEmpty(t)) list.Add(t);
                    }
                }
            }
            return list.Count > 1 ? StorageUtility.CombinePath(list.ToArray()) : "";
        }

        #endregion

    }

}