using System;
using System.Threading;
using System.Threading.Tasks;

namespace MainShell.Common
{
    public class RetryMechanism
    {
        // 自定义异常：操作失败
        public class RetryException : Exception
        {
            public RetryException(string message) : base(message) { }
        }

        public class RetryResult
        {
            public bool Succeeded { get; set; }

            public bool IsAborted { get; set; }

            public int AttemptCount { get; set; }

            public int MaxAttempts { get; set; }

            public string Message { get; set; }
        }

        /// <summary>
        /// 向后兼容旧接口：默认直接继续重试直到达到最大次数，失败时抛异常。
        /// 新代码建议优先使用 <see cref="RetryOrThrow(Func{bool}, Func{int, bool}, string, int)"/> 或返回结果对象的重载。
        /// </summary>
        public static void RetryIfNeeded(Func<bool> checkCondition, string message, int maxAttempts = 3)
        {
            RetryOrThrow(checkCondition, attempt => true, message, maxAttempts);
        }

        /// <summary>
        /// 执行同步重试并返回结果对象。
        /// </summary>
        /// <param name="checkCondition">
        /// 每次尝试时执行的检查逻辑。
        /// 返回 <c>true</c> 表示成功，返回 <c>false</c> 表示本次失败并根据 <paramref name="shouldRetry"/> 决定是否继续。
        /// 如果该委托抛出异常，则异常会直接向外传播，不会被当前重试机制捕获或吞掉，也不会自动进入下一次重试。
        /// </param>
        /// <param name="shouldRetry">当一次尝试失败后，决定是否继续重试。参数为当前已完成的尝试次数，从 1 开始计数。</param>
        /// <param name="message">操作描述信息。</param>
        /// <param name="maxAttempts">最大尝试次数，必须大于 0。</param>
        public static RetryResult RetryIfNeeded(Func<bool> checkCondition, Func<int, bool> shouldRetry, string message, int maxAttempts = 3)
        {
            if (checkCondition == null)
            {
                throw new ArgumentNullException(nameof(checkCondition));
            }

            if (shouldRetry == null)
            {
                throw new ArgumentNullException(nameof(shouldRetry));
            }

            if (maxAttempts <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxAttempts), "maxAttempts must be greater than 0.");
            }

            for (int attempt = 1; attempt <= maxAttempts; attempt++)
            {
                bool conditionMet = checkCondition();
                if (conditionMet)
                {
                    return new RetryResult
                    {
                        Succeeded = true,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }

                if (attempt == maxAttempts)
                {
                    return new RetryResult
                    {
                        Succeeded = false,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }

                bool continueRetry = shouldRetry(attempt);
                if (!continueRetry)
                {
                    return new RetryResult
                    {
                        Succeeded = false,
                        IsAborted = true,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }
            }

            return new RetryResult
            {
                Succeeded = false,
                AttemptCount = maxAttempts,
                MaxAttempts = maxAttempts,
                Message = message
            };
        }

        // 保留抛异常风格的兼容方法
        public static void RetryOrThrow(Func<bool> checkCondition, Func<int, bool> shouldRetry, string message, int maxAttempts = 3)
        {
            var result = RetryIfNeeded(checkCondition, shouldRetry, message, maxAttempts);
            EnsureSuccess(result);
        }

        /// <summary>
        /// 向后兼容旧接口的异步版本：默认直接继续重试直到达到最大次数，失败时抛异常。
        /// 新代码建议优先使用 <see cref="RetryOrThrowAsync(Func{Task{bool}}, Func{int, Task{bool}}, string, int, CancellationToken)"/> 或返回结果对象的重载。
        /// </summary>
        public static async Task RetryIfNeededAsync(Func<Task<bool>> checkConditionAsync, string message, int maxAttempts = 3)
        {
            await RetryOrThrowAsync(checkConditionAsync, attempt => Task.FromResult(true), message, maxAttempts, CancellationToken.None).ConfigureAwait(false);
        }

        /// <summary>
        /// 执行异步重试并返回结果对象。
        /// </summary>
        /// <param name="checkConditionAsync">
        /// 每次尝试时执行的异步检查逻辑。
        /// 返回 <c>true</c> 表示成功，返回 <c>false</c> 表示本次失败并根据 <paramref name="shouldRetryAsync"/> 决定是否继续。
        /// 如果该委托抛出异常，则异常会直接向外传播，不会被当前重试机制捕获或吞掉，也不会自动进入下一次重试。
        /// </param>
        /// <param name="shouldRetryAsync">当一次尝试失败后，决定是否继续重试。参数为当前已完成的尝试次数，从 1 开始计数。</param>
        /// <param name="message">操作描述信息。</param>
        /// <param name="maxAttempts">最大尝试次数，必须大于 0。</param>
        /// <param name="cancellationToken">用于取消异步重试流程。</param>
        public static async Task<RetryResult> RetryIfNeededAsync(Func<Task<bool>> checkConditionAsync, Func<int, Task<bool>> shouldRetryAsync, string message, int maxAttempts = 3, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (checkConditionAsync == null)
            {
                throw new ArgumentNullException(nameof(checkConditionAsync));
            }

            if (shouldRetryAsync == null)
            {
                throw new ArgumentNullException(nameof(shouldRetryAsync));
            }

            if (maxAttempts <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxAttempts), "maxAttempts must be greater than 0.");
            }

            for (int attempt = 1; attempt <= maxAttempts; attempt++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                bool conditionMet = await checkConditionAsync().ConfigureAwait(false);
                if (conditionMet)
                {
                    return new RetryResult
                    {
                        Succeeded = true,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }

                if (attempt == maxAttempts)
                {
                    return new RetryResult
                    {
                        Succeeded = false,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }

                cancellationToken.ThrowIfCancellationRequested();

                bool continueRetry = await shouldRetryAsync(attempt).ConfigureAwait(false);
                if (!continueRetry)
                {
                    return new RetryResult
                    {
                        Succeeded = false,
                        IsAborted = true,
                        AttemptCount = attempt,
                        MaxAttempts = maxAttempts,
                        Message = message
                    };
                }
            }

            return new RetryResult
            {
                Succeeded = false,
                AttemptCount = maxAttempts,
                MaxAttempts = maxAttempts,
                Message = message
            };
        }

        // 保留抛异常风格的异步兼容方法
        public static async Task RetryOrThrowAsync(Func<Task<bool>> checkConditionAsync, Func<int, Task<bool>> shouldRetryAsync, string message, int maxAttempts = 3, CancellationToken cancellationToken = default(CancellationToken))
        {
            var result = await RetryIfNeededAsync(checkConditionAsync, shouldRetryAsync, message, maxAttempts, cancellationToken).ConfigureAwait(false);
            EnsureSuccess(result);
        }

        private static void EnsureSuccess(RetryResult result)
        {
            if (result.Succeeded)
            {
                return;
            }

            if (result.IsAborted)
            {
                throw new RetryException($"{result.Message} 已中止重试，操作失败。尝试次数: {result.AttemptCount}/{result.MaxAttempts}");
            }

            throw new RetryException($"{result.Message} 已达到最大重试次数，操作失败。尝试次数: {result.AttemptCount}/{result.MaxAttempts}");
        }
    }
}
