using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Top.Api
{
    /// 
    /// 调用出错自动重试客户端。
    /// 
    public class AutoRetryTopClient : DefaultTopClient
    {
        private static readonly TopException RETRY_FAIL = new TopException("sdk.retry-call-fail", "API调用重试失败");
        /// 
        /// 单次请求的最大重试次数,默认值为3次。
        /// 
        private int maxRetryCount = 3;
        /// 
        /// 重试之前休眠时间,默认值为100毫秒。
        /// 
        private int retryWaitTime = 100;
        /// 
        /// 超过最大重试次数时是否抛出异常。
        /// 
        private bool throwIfOverMaxRetry = false;
        /// 
        /// 自定义重试错误码列表。
        /// 
        private IDictionary retryErrorCodes;
        public AutoRetryTopClient(string serverUrl, string appKey, string appSecret)
            : base(serverUrl, appKey, appSecret)
        {
        }
        public AutoRetryTopClient(string serverUrl, string appKey, string appSecret, string format)
            : base(serverUrl, appKey, appSecret, format)
        {
        }
        public override T Execute(ITopRequest request)
        {
            return Execute(request, null);
        }
        public override T Execute(ITopRequest request, string session)
        {
            return Execute(request, session, DateTime.Now);
        }
        public override T Execute(ITopRequest request, string session, DateTime timestamp)
        {
            T rsp = null;
            TopException exp = null;
            for (int i = 0; i < maxRetryCount; i++)
            {
                if (i > 0)
                {
                    if ((rsp != null && ((rsp.SubErrCode != null && rsp.SubErrCode.StartsWith("isp."))
                        || (retryErrorCodes != null && retryErrorCodes.ContainsKey(rsp.SubErrCode)))) || exp != null)
                    {
                        Thread.Sleep(retryWaitTime);
                        topLogger.Warn(BuildRetryLog(request.GetApiName(), request.GetParameters(), i));
                    }
                    else
                    {
                        break;
                    }
                }
                try
                {
                    rsp = base.Execute(request, session);
                    if (rsp.IsError)
                    {
                        if (i == maxRetryCount && throwIfOverMaxRetry)
                        {
                            throw RETRY_FAIL;
                        }
                    }
                    else
                    {
                        return rsp;
                    }
                }
                catch (TopException e)
                {
                    if (exp == null)
                    {
                        exp = e;
                    }
                }
            }
            if (exp != null)
            {
                throw exp;
            }
            else
            {
                return rsp;
            }
        }
        public void SetMaxRetryCount(int maxRetryCount)
        {
            this.maxRetryCount = maxRetryCount;
        }
        public void SetRetryWaitTime(int retryWaitTime)
        {
            this.retryWaitTime = retryWaitTime;
        }
        public void SetThrowIfOverMaxRetry(bool throwIfOverMaxRetry)
        {
            this.throwIfOverMaxRetry = throwIfOverMaxRetry;
        }
        public void AddRetryErrorCode(string errorCode)
        {
            if (this.retryErrorCodes == null)
            {
                this.retryErrorCodes = new Dictionary();
            }
            this.retryErrorCodes.Add(errorCode, false);
        }
        private string BuildRetryLog(string apiName, IDictionary parameters, int retryCount)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(apiName).Append(" retry call ").Append(retryCount);
            if (parameters.ContainsKey("fields"))
            {
                parameters.Remove("fields");
            }
            sb.Append(" times, parameters=").Append(parameters);
            return sb.ToString();
        }
    }
}