// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技（广东）有限公司所有。
// 所有权利保留。
// 官方网站：https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------

namespace ThingsGateway.RescuePolicy;

/// <summary>
///     重试策略
/// </summary>
public sealed class RetryPolicy : RetryPolicy<object>
{
    /// <summary>
    ///     <inheritdoc cref="RetryPolicy" />
    /// </summary>
    public RetryPolicy()
    {
    }

    /// <summary>
    ///     <inheritdoc cref="RetryPolicy" />
    /// </summary>
    /// <param name="maxRetryCount">最大重试次数</param>
    public RetryPolicy(int maxRetryCount)
        : base(maxRetryCount)
    {
    }
}

/// <summary>
///     重试策略
/// </summary>
/// <typeparam name="TResult">操作返回值类型</typeparam>
public class RetryPolicy<TResult> : PolicyBase<TResult>
{
    /// <summary>
    ///     等待重试输出信息
    /// </summary>
    internal const string WAIT_RETRY_MESSAGE = "Retry after {0} seconds.";

    /// <summary>
    ///     重试输出信息
    /// </summary>
    internal const string RETRY_MESSAGE = "Retrying for the {0}nd time.";

    /// <summary>
    ///     <inheritdoc cref="RetryPolicy{TResult}" />
    /// </summary>
    public RetryPolicy()
    {
    }

    /// <summary>
    ///     <inheritdoc cref="RetryPolicy{TResult}" />
    /// </summary>
    /// <param name="maxRetryCount">最大重试次数</param>
    public RetryPolicy(int maxRetryCount) => MaxRetryCount = maxRetryCount;

    /// <summary>
    ///     最大重试次数
    /// </summary>
    public int MaxRetryCount { get; set; }

    /// <summary>
    ///     重试等待时间集合
    /// </summary>
    public TimeSpan[]? RetryIntervals { get; set; }

    /// <summary>
    ///     捕获的异常集合
    /// </summary>
    public HashSet<Type>? HandleExceptions { get; set; }

    /// <summary>
    ///     捕获的内部异常集合
    /// </summary>
    public HashSet<Type>? HandleInnerExceptions { get; set; }

    /// <summary>
    ///     操作结果条件集合
    /// </summary>
    public List<Func<RetryPolicyContext<TResult>, bool>>? ResultConditions { get; set; }

    /// <summary>
    ///     等待重试时操作方法
    /// </summary>
    public Action<RetryPolicyContext<TResult>, TimeSpan>? WaitRetryAction { get; set; }

    /// <summary>
    ///     重试时操作方法
    /// </summary>
    public Action<RetryPolicyContext<TResult>>? RetryingAction { get; set; }

    /// <summary>
    ///     添加捕获异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> Handle<TException>()
        where TException : System.Exception
    {
        HandleExceptions ??= [];
        HandleExceptions.Add(typeof(TException));

        return this;
    }

    /// <summary>
    ///     添加捕获异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <param name="exceptionCondition">异常条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> Handle<TException>(Func<TException, bool> exceptionCondition)
        where TException : System.Exception
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(exceptionCondition);

        // 添加捕获异常类型和条件
        Handle<TException>();
        HandleResult(context => context.Exception is TException exception && exceptionCondition(exception));

        return this;
    }

    /// <summary>
    ///     添加捕获异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> Or<TException>()
        where TException : System.Exception =>
        Handle<TException>();

    /// <summary>
    ///     添加捕获异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <param name="exceptionCondition">异常条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> Or<TException>(Func<TException, bool> exceptionCondition)
        where TException : System.Exception =>
        Handle(exceptionCondition);

    /// <summary>
    ///     添加捕获内部异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> HandleInner<TException>()
        where TException : System.Exception
    {
        HandleInnerExceptions ??= [];
        HandleInnerExceptions.Add(typeof(TException));

        return this;
    }

    /// <summary>
    ///     添加捕获内部异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <param name="exceptionCondition">异常条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> HandleInner<TException>(Func<TException, bool> exceptionCondition)
        where TException : System.Exception
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(exceptionCondition);

        // 添加捕获内部异常类型和条件
        HandleInner<TException>();
        HandleResult(context =>
            context.Exception?.InnerException is TException exception && exceptionCondition(exception));

        return this;
    }

    /// <summary>
    ///     添加捕获内部异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> OrInner<TException>()
        where TException : System.Exception =>
        HandleInner<TException>();

    /// <summary>
    ///     添加捕获内部异常类型
    /// </summary>
    /// <typeparam name="TException">
    ///     <see cref="System.Exception" />
    /// </typeparam>
    /// <param name="exceptionCondition">异常条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> OrInner<TException>(Func<TException, bool> exceptionCondition)
        where TException : System.Exception =>
        HandleInner(exceptionCondition);

    /// <summary>
    ///     添加操作结果条件
    /// </summary>
    /// <param name="resultCondition">操作结果条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> HandleResult(Func<RetryPolicyContext<TResult>, bool> resultCondition)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(resultCondition);

        ResultConditions ??= [];
        ResultConditions.Add(resultCondition);

        return this;
    }

    /// <summary>
    ///     添加操作结果条件
    /// </summary>
    /// <param name="resultCondition">操作结果条件</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> OrResult(Func<RetryPolicyContext<TResult>, bool> resultCondition) =>
        HandleResult(resultCondition);

    /// <summary>
    ///     添加重试等待时间
    /// </summary>
    /// <param name="retryIntervals">重试等待时间</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> WaitAndRetry(params TimeSpan[] retryIntervals)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(retryIntervals);

        RetryIntervals = retryIntervals;

        return this;
    }

    /// <summary>
    ///     永久重试
    /// </summary>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> Forever()
    {
        MaxRetryCount = int.MaxValue;

        return this;
    }

    /// <summary>
    ///     永久重试并添加重试等待时间
    /// </summary>
    /// <param name="retryIntervals">重试等待时间</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> WaitAndRetryForever(params TimeSpan[] retryIntervals) =>
        WaitAndRetry(retryIntervals)
            .Forever();

    /// <summary>
    ///     添加等待重试时操作方法
    /// </summary>
    /// <param name="waitRetryAction">等待重试时操作方法</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> OnWaitRetry(Action<RetryPolicyContext<TResult>, TimeSpan> waitRetryAction)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(waitRetryAction);

        WaitRetryAction = waitRetryAction;

        return this;
    }

    /// <summary>
    ///     添加重试时操作方法
    /// </summary>
    /// <param name="retryingAction">重试时操作方法</param>
    /// <returns>
    ///     <see cref="RetryPolicy{TResult}" />
    /// </returns>
    public RetryPolicy<TResult> OnRetrying(Action<RetryPolicyContext<TResult>> retryingAction)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(retryingAction);

        RetryingAction = retryingAction;

        return this;
    }

    /// <summary>
    ///     检查是否满足捕获异常的条件
    /// </summary>
    /// <param name="context">
    ///     <see cref="RetryPolicyContext{TResult}" />
    /// </param>
    /// <returns>
    ///     <see cref="bool" />
    /// </returns>
    internal bool ShouldHandle(RetryPolicyContext<TResult> context)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(context);

        // 检查最大重试次数是否大于等于 0
        if (MaxRetryCount <= 0)
        {
            return false;
        }

        // 检查重试次数是否大于最大重试次数减
        if (context.RetryCount > MaxRetryCount - 1)
        {
            return false;
        }

        // 检查是否满足捕获异常的条件
        if (CanHandleException(context, HandleExceptions, context.Exception)
            || CanHandleException(context, HandleInnerExceptions, context.Exception?.InnerException))
        {
            return true;
        }

        // 检查是否满足操作结果条件
        return ResultConditions is { Count: > 0 } && ResultConditions.Any(condition => condition(context));
    }

    /// <inheritdoc />
    public override async Task<TResult?> ExecuteAsync(Func<CancellationToken, Task<TResult?>> operation,
        CancellationToken cancellationToken = default)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(operation);

        // 初始化重试策略上下文
        var context = new RetryPolicyContext<TResult> { PolicyName = PolicyName };

        // 无限循环直到满足条件退出
        while (true)
        {
            try
            {
                // 获取操作方法执行结果
                context.Result = await operation(cancellationToken).ConfigureAwait(false);
                context.Exception = null;
            }
            catch (System.Exception exception)
            {
                // 设置策略上下文异常信息
                context.Exception = exception;
            }

            // 检查是否存在取消请求
            cancellationToken.ThrowIfCancellationRequested();

            // 检查是否满足捕获异常的条件
            if (!ShouldHandle(context))
            {
                // 返回结果或抛出异常
                return ReturnOrThrowIfException(context);
            }

            // 递增上下文数据
            context.Increment();

            // 检查是否配置了重试时间
            if (RetryIntervals is { Length: > 0 })
            {
                // 解析延迟时间戳
                var delay = RetryIntervals[context.RetryCount % RetryIntervals.Length];

                // 输出调试事件
                Debugging.Info(WAIT_RETRY_MESSAGE, delay.TotalSeconds);

                // 调用等待重试时操作方法
                WaitRetryAction?.Invoke(context, delay);

                // 延迟指定时间再操作
                await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
            }

            // 输出调试事件
            Debugging.Warn(RETRY_MESSAGE, context.RetryCount);

            // 调用重试时操作方法
            RetryingAction?.Invoke(context);
        }
    }

    /// <summary>
    ///     检查是否满足捕获异常的条件
    /// </summary>
    /// <param name="context">
    ///     <see cref="RetryPolicyContext{TResult}" />
    /// </param>
    /// <param name="exceptionTypes">捕获异常类型集合</param>
    /// <param name="exception">
    ///     <see cref="System.Exception" />
    /// </param>
    /// <returns>
    ///     <see cref="bool" />
    /// </returns>
    internal bool CanHandleException(RetryPolicyContext<TResult> context
        , HashSet<Type>? exceptionTypes
        , System.Exception? exception)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(context);

        // 空检查
        if (exception is null)
        {
            return false;
        }

        // 检查是否满足捕获异常的条件
        if (exceptionTypes is not (null or { Count: 0 })
            && !exceptionTypes.Any(ex => ex.IsInstanceOfType(exception)))
        {
            return false;
        }

        // 检查是否满足操作结果条件
        return ResultConditions is not { Count: > 0 } || ResultConditions.Any(condition => condition(context));
    }

    /// <summary>
    ///     返回结果或抛出异常
    /// </summary>
    /// <param name="context">
    ///     <see cref="RetryPolicyContext{TResult}" />
    /// </param>
    /// <returns>
    ///     <typeparamref name="TResult" />
    /// </returns>
    internal static TResult? ReturnOrThrowIfException(RetryPolicyContext<TResult> context)
    {
        // 空检查
        ArgumentNullException.ThrowIfNull(context);

        // 空检查
        if (context.Exception is not null)
        {
            throw context.Exception;
        }

        return context.Result;
    }
}