using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Polly8Study.Test
{
    public class Polly8FallbackStrategyTest
    {
        private readonly ITestOutputHelper _output;

        public Polly8FallbackStrategyTest(ITestOutputHelper testOutput)
        {
            _output = testOutput;
        }

        [Fact]
        public async void Test()
        {
            FallbackStrategyOptions<int> optionsOnFallback = new FallbackStrategyOptions<int>
            {
                ShouldHandle = new PredicateBuilder<int>()
                    .Handle<Exception>()
                    .HandleResult(r => r > 50),

                FallbackAction = static args =>
                {
                    var v = Random.Shared.Next(1, 50);
                    return Outcome.FromResultAsValueTask(v);
                },

                OnFallback = args =>
                {
                    _output.WriteLine(args.Outcome.Result.ToString());
                    // Add extra logic to be executed when the fallback is triggered, such as logging.
                    return default; // Returns an empty ValueTask
                }
            };

            // Add a fallback strategy with a FallbackStrategyOptions<TResult> instance to the pipeline
            var pipeline = new ResiliencePipelineBuilder<int>()
                 .AddFallback(optionsOnFallback)
                 .Build();

            var data = await pipeline.ExecuteAsync<int>(static async (innerToken) =>
             {

                 return await Task.FromResult(Random.Shared.Next(1, 100));

             });

            _output.WriteLine(data.ToString());

        }

        /// <summary>
        /// 生成回退选项
        /// 注意:一定是有返回值的,否则没意义
        /// </summary>
        [Fact]
        public async Task Create_FallbackStrategyOption_TestAsync()
        {
            //以返回简单的字符串或数据为例

            // 1、操作失败(异常或返回值不满足),则回退为固定值
            var optionsSubstitute = new FallbackStrategyOptions<string>
            {
                //设置回退条件
                ShouldHandle = new PredicateBuilder<string>()
                    //出现指定的异常类型时,回退
                    .Handle<Exception>()
                    //返回值满足条件时,回退
                    .HandleResult(r => r is null),

                //回退时,返回的替代值
                FallbackAction = static args => Outcome.FromResultAsValueTask("回退时的值")
            };

            // 2、如果操作失败,则回退为动态生成值
            var optionsFallbackAction = new FallbackStrategyOptions<string>
            {
                //设置回退条件
                ShouldHandle = new PredicateBuilder<string>()
                    //出现指定的异常类型时,回退
                    .Handle<Exception>()
                    //返回值满足条件时,回退
                    .HandleResult(r => r is null),

                FallbackAction = static args =>
                {
                    var text = Random.Shared.Next(100, 999).ToString();
                    return Outcome.FromResultAsValueTask(text);
                }
            };

            // 3、使用默认值或动态生成的值,如果触发回退,则执行其他操作。
            var optionsOnFallback = new FallbackStrategyOptions<string>
            {
                ShouldHandle = new PredicateBuilder<string>()
                    //出现指定的异常类型时,回退
                    .Handle<Exception>()
                    //返回值满足条件时,回退
                    .HandleResult(r => r is null),

                //回退时:返回替代值
                FallbackAction = static args =>
                {
                    var text = Random.Shared.Next(100, 999).ToString();
                    return Outcome.FromResultAsValueTask(text);
                },

                //回退时:执行额外方法
                OnFallback = static args =>
                {
                    //从参数里可以拿到上下文和结果
                    Console.WriteLine(args.Outcome.Result);

                    // 添加在触发回退时要执行的额外逻辑,例如日志记录。
                    return default; // Returns an empty ValueTask
                }
            };

            // 添加到弹性管道
            var pipeline = new ResiliencePipelineBuilder<string>()
                 .AddFallback(optionsOnFallback)
                 .Build();

            //使用弹性管道,执行/调用 方法
            var data = await pipeline.ExecuteAsync<string>(static async (innerToken) =>
            {
                var data = Random.Shared.Next(1,100);

                if (data % 0 ==0)
                {
                    throw new Exception("");
                }

                return await ValueTask.FromResult(data.ToString());
            });

            //断言
            Assert.NotEmpty(data);
        }

        /// <summary>
        /// 生成回退选项
        /// 注意:一定是有返回值的,否则没意义
        /// </summary>
        [Fact]
        public async Task FallbackStrategyOption_TestAsync()
        {
            //方法返回 1-00 随机数,使用回退方法确保返回0-50的数据。

            var optionsOnFallback = new FallbackStrategyOptions<int>
            {
                ShouldHandle = new PredicateBuilder<int>()
                    .Handle<Exception>()
                    .HandleResult(r => r >= 50),

                //回退时:返回替代值
                FallbackAction = args =>
                {
                    var v = Random.Shared.Next(1, 50);

                    _output.WriteLine($"原方法生成的数字 {args.Outcome.Result},大于50,使用回退替代值 {v}");

                    return Outcome.FromResultAsValueTask(v);
                },

                //回退时:执行额外方法(先于FallbackAction执行)
                OnFallback = args =>
                {
                    //从参数里可以拿到上下文和结果
                    _output.WriteLine($"回退额外方法执行,原方法生成数字为:{args.Outcome.Result}");

                    // 添加在触发回退时要执行的额外逻辑,例如日志记录。
                    return default; // Returns an empty ValueTask
                }
            };

            // Add a fallback strategy with a FallbackStrategyOptions<TResult> instance to the pipeline
            var pipeline = new ResiliencePipelineBuilder<int>()
                 .AddFallback(optionsOnFallback)
                 .Build();

            var data = await pipeline.ExecuteAsync<int>(async (innerToken) =>
             {
                 //随机返回1到100数字
                 var v = Random.Shared.Next(1, 100);
                 _output.WriteLine($"方法生成数字为:{v}");

                 return await Task.FromResult(v);

             });

            Assert.True(data <= 50);
        }

    }
}