using LitJson;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

namespace WS
{
    ///<summary>Header 实体</summary>
    public class ResponseHeader
    {
        ///<summary>名称(Key)</summary>
        public string Name { get; set; }

        ///<summary>值</summary>
        public string Value { get; set; }
    }

    [ObjectSystem]
    public class RequestComponentAwakeSystem : AwakeSystem<RequestComponent>
    {
        public override void Awake(RequestComponent self)
        {
            self.Awake();
        }
    }

    ///<summary>Web请求管理</summary>
    public class RequestComponent : SingleComponent<RequestComponent>
    {
        //请求超时时间
        public const int OutTime = 10;
        //所有Header
        private readonly Dictionary<string, ResponseHeader> Headers = new Dictionary<string, ResponseHeader>();
        //默认Header
        private readonly List<string> DefaultHeaders = new List<string>();
        //错误码
        private readonly static Dictionary<int, string> ErrorCode = new Dictionary<int, string>();

        public void Awake()
        {
            AddErrorCode(500, "服务器内部错误!");
            AddErrorCode(405, "接口错误!");
            AddErrorCode(404, "请求无效!");
            AddErrorCode(403, "权限不足!");
            AddErrorCode(401, "未登录!");
            AddErrorCode(0, "请求超时,服务器未响应!");
        }

        #region -- Header --

        /// <summary>添加/修改 Header</summary>
        /// <param name="name">名称</param>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <param name="isdefault">设置为默认Header，回自动在请求时添加</param>
        public void AddHeader(string name, string key, string value, bool isdefault = false)
        {
            if (!Headers.ContainsKey(name))
            {
                ResponseHeader header = new ResponseHeader();
                Headers.Add(name, header);
            }
            Headers[name].Name = key;
            Headers[name].Value = value;
            if (isdefault)
            {
                if (!DefaultHeaders.Contains(name))
                {
                    DefaultHeaders.Add(name);
                }
            }
        }

        /// <summary>移除 Header</summary>
        /// <param name="name">名称</param>
        public void RemoveHeader(string name)
        {
            if (Headers.ContainsKey(name))
            {
                Headers.Remove(name);
                if (DefaultHeaders.Contains(name))
                {
                    DefaultHeaders.Remove(name);
                }
            }
        }

        /// <summary>移除所有 Header</summary>
        public void RemoveAllHeader()
        {
            Headers.Clear();
            DefaultHeaders.Clear();
        }

        /// <summary>获取 Header</summary>
        /// <param name="name">名称</param>
        public ResponseHeader GetHeader(string name)
        {
            if (Headers.ContainsKey(name))
            {
                return Headers[name];
            }
            return null;
        }

        /// <summary>设置 Header</summary>
        /// <param name="header">存储header的名称</param>
        /// <param name="webrequest">请求代理</param>
        public void SetHeader(string[] header, UnityWebRequest webrequest)
        {
            for (int i = 0; i < DefaultHeaders.Count; i++)
            {
                ResponseHeader rh = GetHeader(DefaultHeaders[i]);
                webrequest.SetRequestHeader(rh.Name, rh.Value);
            }
            if (header.Length > 0)
            {
                for (int i = 0; i < header.Length; i++)
                {
                    if (!DefaultHeaders.Contains(header[i]))
                    {
                        ResponseHeader rh = GetHeader(header[i]);
                        webrequest.SetRequestHeader(rh.Name, rh.Value);
                    }
                }
            }
        }

        #endregion

        #region -- ErrorCode--

        ///<summary>添加请求错误拦截状态码(只有异步请求有效)</summary>
        ///<param name="code">状态码</param>
        ///<param name="msg">提示消息</param>
        public void AddErrorCode(int code, string msg)
        {
            if (!ErrorCode.ContainsKey(code))
            {
                ErrorCode.Add(code, msg);
            }
        }

        #endregion

        #region -- Get --

        /// <summary>Get 请求</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="oncallback">请求回调</param>
        /// <param name="header">头</param>
        public static void Get<T>(string url, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            TimerComponent.Instance.StartCoroutine(MonoGet<T>(url, oncallback, header));
        }

        /// <summary>Get 请求文件</summary>
        /// <param name="url">请求地址</param>
        /// <param name="oncallback">请求回调</param>
        /// <param name="header">头</param>
        public static void Get(string url, UnityAction<byte[]> oncallback, params string[] header)
        {
            TimerComponent.Instance.StartCoroutine(MonoGetFile(url, oncallback, header));
        }

        ///<summary>Get 请求</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="header">头</param>
        public static async Task<ResponseOutput<T>> GetAsync<T>(string url, params string[] header)
        {
            RequestAsync request = ComponentFactory.Create<RequestAsync>();
            await request.GetAsync(url, header);
            ResponseOutput<T> output = ParseRequest<T>(request);

            return output;
        }

        ///<summary>Get 请求文件</summary>
        /// <param name="url">请求地址</param>
        /// <param name="header">头</param>
        public static async Task<byte[]> GetAsync(string url, params string[] header)
        {
            RequestAsync request = ComponentFactory.Create<RequestAsync>();
            await request.GetAsync(url, header);
            if (!string.IsNullOrEmpty(request.Request.error))
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogError($"HTTP请求错误！地址：{request.Request.uri} 消息：{request.Request.error} Code：{request.Request.responseCode}");
#endif
            }
            else
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogDebug(request.Request.uri + "   数据Length:" + request.Request.downloadHandler.data.Length, WSLogType.绿);
#endif
            }
            byte[] buffer = request.Request.downloadHandler.data;
            request.Dispose();
            return buffer;
        }

        //Get 请求
        private static IEnumerator MonoGet<T>(string url, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            UnityWebRequest webrequest = UnityWebRequest.Get(url);
            webrequest.timeout = OutTime;
            Instance.SetHeader(header, webrequest);
            yield return webrequest.SendWebRequest();
            ParseRequest(oncallback, webrequest);
        }

        //Get 请求文件
        private static IEnumerator MonoGetFile(string url, UnityAction<byte[]> oncallback, params string[] header)
        {
            UnityWebRequest webrequest = UnityWebRequest.Get(url);
            webrequest.timeout = OutTime;
            Instance.SetHeader(header, webrequest);
            yield return webrequest.SendWebRequest();
            ResponseOutput<byte[]> output = new ResponseOutput<byte[]>();
            if (!string.IsNullOrEmpty(webrequest.error))
            {
#if UNITY_EDITOR || DEBUG_MODEL
                Log.LogError($"HTTP请求错误！地址：{webrequest.uri} 消息：{webrequest.error} Code：{webrequest.responseCode}");
#endif
            }
            else
            {
#if UNITY_EDITOR || DEBUG_MODEL
                Log.LogDebug(webrequest.uri + "   数据Length:" + webrequest.downloadHandler.data.Length, WSLogType.绿);
#endif
                oncallback.Invoke(webrequest.downloadHandler.data);
            }
            webrequest.Dispose();
        }

        #endregion

        #region -- Post --

        #region -- Json --

        /// <summary>Post Json提交</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="json">Json 数据</param>
        /// <param name="oncallback">请求回调</param>
        /// <param name="header">头</param>
        public static void Post<T>(string url, string json, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            TimerComponent.Instance.StartCoroutine(MonoPost<T>(url, json, oncallback, header));
        }

        ///<summary>Post请求</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="json">Json 数据</param>
        /// <param name="header">头</param>
        public static async Task<ResponseOutput<T>> PostAsync<T>(string url, string json, params string[] header)
        {
            RequestAsync request = ComponentFactory.Create<RequestAsync>();
            await request.PostAsync(url, json, header);
            ResponseOutput<T> output = ParseRequest<T>(request);
            return output;
        }

        //Post Json提交
        private static IEnumerator MonoPost<T>(string url, string json, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            byte[] postBytes = System.Text.Encoding.Default.GetBytes(json);
            UnityWebRequest webrequest = UnityWebRequest.Post(url, UnityWebRequest.kHttpVerbPOST);
            webrequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes);
            webrequest.SetRequestHeader("Content-Type", "application/json");
            webrequest.timeout = OutTime;
            Instance.SetHeader(header, webrequest);
            yield return webrequest.SendWebRequest();
            ParseRequest(oncallback, webrequest);
        }

        #endregion

        #region -- Form --

        /// <summary>Post 表单提交</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="form">表单</param>
        /// <param name="oncallback">请求回调</param>
        /// <param name="header">头</param>
        public static void Post<T>(string url, WWWForm form, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            TimerComponent.Instance.StartCoroutine(MonoPost<T>(url, form, oncallback, header));
        }

        ///<summary>Post请求</summary>
        /// <typeparam name="T">请求返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="form">表单</param>
        /// <param name="header">头</param>
        public static async Task<ResponseOutput<T>> PostAsync<T>(string url, WWWForm form, params string[] header)
        {
            RequestAsync request = ComponentFactory.Create<RequestAsync>();
            await request.PostAsync(url, form, header);
            ResponseOutput<T> output = ParseRequest<T>(request);
            return output;
        }

        //Post 表单提交
        private static IEnumerator MonoPost<T>(string url, WWWForm form, UnityAction<ResponseOutput<T>> oncallback, params string[] header)
        {
            UnityWebRequest webrequest = UnityWebRequest.Post(url, form);
            webrequest.timeout = OutTime;
            Instance.SetHeader(header, webrequest);
            yield return webrequest.SendWebRequest();
            ParseRequest(oncallback, webrequest);
        }

        #endregion

        #endregion

        #region -- Parse --

        ///<summary>解析请求</summary>
        ///<param name="oncallback">回调</param>
        ///<param name="webrequest">请求代理</param>
        private static void ParseRequest<T>(UnityAction<ResponseOutput<T>> oncallback, UnityWebRequest webrequest)
        {
            ResponseOutput<T> output = new ResponseOutput<T>();
            if (!string.IsNullOrEmpty(webrequest.error))
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogError($"HTTP请求错误！地址：{webrequest.uri} 消息：{webrequest.error} Code：{webrequest.responseCode}");
#endif
                oncallback.Invoke(output.NetworkError(webrequest.error, (int)webrequest.responseCode));
            }
            else
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogDebug(webrequest.uri + "   " + webrequest.downloadHandler.text, WSLogType.绿);
#endif
                output = JsonMapper.ToObject<ResponseOutput<T>>(webrequest.downloadHandler.text);
                oncallback.Invoke(output);
            }
            webrequest.Dispose();
        }

        ///<summary>解析请求</summary>
        ///<param name="webrequest">请求代理</param>
        private static ResponseOutput<T> ParseRequest<T>(RequestAsync request)
        {
            ResponseOutput<T> output = new ResponseOutput<T>();
            if (!string.IsNullOrEmpty(request.Request.error))
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogError($"HTTP请求错误！地址：{request.Request.uri} 消息：{request.Request.error} Code：{request.Request.responseCode}");
#endif
                output.NetworkError(request.Request.error, (int)request.Request.responseCode);
            }
            else
            {
#if UNITY_EDITOR||DEBUG_MODEL
                Log.LogDebug(request.Request.uri + "   " + request.Request.downloadHandler.text, WSLogType.绿);
#endif
                output = JsonMapper.ToObject<ResponseOutput<T>>(request.Request.downloadHandler.text);
            }
            request.Dispose();
            return output;
        }

        /// <summary>排除错误码</summary>
        /// <param name="output">请求数据</param>
        /// <param name="method">方法</param>
        /// <param name="butname">请求错误后提示模板可以开启第二个按钮(按钮名称)</param>
        /// <param name="oncallback">按钮回调</param>
        public static bool ParseErrorCode(IResponseOutput output, UnityAction method, string butname = "", UnityAction oncallback = null)
        {
            if (ErrorCode.ContainsKey(output.code))
            {
                string msg = ErrorCode[output.code];
                MessagerContorller.OpenMessageView(MessagerType.Error, msg, "重试", () => { method.Invoke(); }, butname, oncallback);
                return true;
            }
            return false;
        }

        #endregion

        public override void Dispose()
        {
            base.Dispose();
            Headers.Clear();
            DefaultHeaders.Clear();
            ErrorCode.Clear();
        }
    }
}