namespace Polly8Study.Test
{
    /// <summary>
    /// Polly8超时策略 测试
    /// 关键:
    ///     CancellationToken,没有这个是不起使用的
    ///     就是之前版本中的乐观超时,悲观超时貌似取消了
    /// </summary>
    public class Polly8TimeoutTest
    {
        private readonly ITestOutputHelper _output;

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

        /// <summary>
        /// 超时策略选项
        /// </summary>
        [Fact]
        public void TimeoutStrategyOptions_Test()
        {
            //直接设置
            new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromMilliseconds(100))
                .Build();

            //默认值
            var defaultOption = new TimeoutStrategyOptions();

            //全功能
            TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
            {
                //名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
                Name = "timeout",

                //固定超时时间(启用了TimeoutGenerator,则无效)
                Timeout = TimeSpan.FromSeconds(2),

                //动态超时时间设置
                //TimeoutGenerator =  arg => 
                //{
                //    var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));

                //    return ValueTask.FromResult(ts);
                //},

                //发生超时时引发的超时委托
                OnTimeout = (args) =>
                {
                    var key = args.Context.OperationKey;
                    _output.WriteLine("OnTimeout");
                    return ValueTask.CompletedTask;
                },
            };

            //使用
            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(timeoutOption)
                .Build();
        }

        /// <summary>
        /// 非可取消任务:忽略超时
        /// 同步执行
        /// </summary>
        [Fact]
        public void No_CancellationToken_Test()
        {
            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromSeconds(1))
                .Build();

            //外部tcs可省略
            var tcs = new CancellationTokenSource();
            try
            {
                pipeline.Execute
                (
                    //此处必须是可取消的任务,否则超时不作为(一直等待)
                    callback: (CancellationToken innerToken) =>
                    {
                        //虽然接口耗时大,因为没有使用‘可取消的’ Send请求,超时设置被忽略,会等待接口正确返回
                        HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");

                        //关键:Send方法 没有传入参数 cancellationToken
                        //其它同步方法,只要是可取消方法均可
                        HttpResponseMessage response = new HttpClient().Send(requestMessage);
                        if (response.IsSuccessStatusCode == false)
                        {
                            _output.WriteLine($"非正常响应,响应码:{response.StatusCode}");
                            return;
                        }
                        var stream = response.Content.ReadAsStream();
                        var bs = new byte[stream.Length];
                        stream.Read(bs, 0, (int)stream.Length);

                        var text = System.Text.UTF8Encoding.UTF8.GetString(bs);

                        _output.WriteLine($"响应内容:{text}");

                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: tcs.Token
                );
            }
            catch (OperationCanceledException ex)
            {
                _output.WriteLine($"任务取消,引发异常:{ex.Message}");
            }
            catch (TimeoutRejectedException ex)
            {
                _output.WriteLine($"超时,引发异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                _output.WriteLine($"API服务异常:{ex.Message}");
            }
            finally
            {
                tcs.TryReset();
            }
        }

        /// <summary>
        /// 可取消任务:超时
        /// 同步执行
        /// </summary>
        [Fact]
        public void Has_CancellationToken_Timeout_Test()
        {
            TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
            {
                //名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
                Name = "timeout",

                //固定超时时间(启用了TimeoutGenerator,则无效)
                Timeout = TimeSpan.FromSeconds(1),

                //动态超时时间设置
                //TimeoutGenerator =  arg => 
                //{
                //    var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));

                //    return ValueTask.FromResult(ts);
                //},

                //发生超时时引发的超时委托
                OnTimeout = (args) =>
                {
                    _output.WriteLine("OnTimeout 超时委托执行");
                    return ValueTask.CompletedTask;
                },
            };

            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(timeoutOption)
                .Build();

            var tcs = new CancellationTokenSource();
            try
            {
                pipeline.Execute
                (
                    //此处必须是可取消的任务,否则超时不作为(一直等待)
                    callback: (innerToken) =>
                    {
                        HttpRequestMessage r = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");
                        var response = new HttpClient().Send(r, innerToken);
                        if (response.IsSuccessStatusCode == false)
                        {
                            Console.WriteLine("非正常响应");
                            return;
                        }
                        var stream = response.Content.ReadAsStream();
                        var bs = new byte[stream.Length];
                        stream.Read(bs, 0, (int)stream.Length);

                        var text = System.Text.UTF8Encoding.UTF8.GetString(bs);

                        _output.WriteLine($"响应内容:{text}");
                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: tcs.Token
                );
            }
            catch (OperationCanceledException ex)
            {
                _output.WriteLine($"任务取消,引发异常:{ex.Message}");
            }
            catch (TimeoutRejectedException ex)
            {
                _output.WriteLine($"超时,引发异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                _output.WriteLine($"API服务异常:{ex.Message}");
            }
            finally
            {
                tcs.TryReset();
            }
        }

        /// <summary>
        /// 可取消任务:不超时
        /// 同步执行
        /// </summary>
        [Fact]
        public void Has_CancellationToken_NotTimeout_Test()
        {
            TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
            {
                //名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
                Name = "timeout",

                //固定超时时间(启用了TimeoutGenerator,则无效)
                Timeout = TimeSpan.FromSeconds(5),

                //动态超时时间设置
                //TimeoutGenerator =  arg => 
                //{
                //    var ts = TimeSpan.FromSeconds(Math.Pow(2, 1));

                //    return ValueTask.FromResult(ts);
                //},

                //发生超时时引发的超时委托
                OnTimeout = (args) =>
                {
                    Console.WriteLine("OnTimeout 超时委托执行");
                    return ValueTask.CompletedTask;
                },
            };

            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(timeoutOption)
                .Build();

            var tcs = new CancellationTokenSource();
            try
            {
                pipeline.Execute
                (
                    //此处必须是可取消的任务,否则超时不作为(一直等待)
                    callback: (innerToken) =>
                    {
                        HttpRequestMessage r = new HttpRequestMessage(HttpMethod.Get, "http://localhost:44344/api/Timeout/Slow");
                        var response = new HttpClient().Send(r, innerToken);
                        if (response.IsSuccessStatusCode == false)
                        {
                            Console.WriteLine("非正常响应");
                            return;
                        }
                        var stream = response.Content.ReadAsStream();
                        var bs = new byte[stream.Length];
                        stream.Read(bs, 0, (int)stream.Length);

                        var text = System.Text.UTF8Encoding.UTF8.GetString(bs);

                        _output.WriteLine($"响应内容:{text}");
                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: tcs.Token
                );
            }
            catch (OperationCanceledException ex)
            {
                _output.WriteLine($"任务取消,引发异常:{ex.Message}");
            }
            catch (TimeoutRejectedException ex)
            {
                _output.WriteLine($"超时,引发异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                _output.WriteLine($"API服务异常:{ex.Message}");
            }
            finally
            {
                tcs.TryReset();
            }

        }

        /// <summary>
        /// 不可取消任务:忽略超时
        /// 异步执行
        /// </summary>
        [Fact]
        public async void No_CancellationToken_ExecuteAsync_Test()
        {
            TimeoutStrategyOptions timeoutOption = new TimeoutStrategyOptions
            {
                //名称唯一标识了特定策略的特定实例,也包含在由各个弹性策略生成的遥测数据中
                Name = "timeout",

                //固定超时时间(启用了TimeoutGenerator,则无效)
                Timeout = TimeSpan.FromSeconds(1),


                //发生超时时引发的超时委托
                OnTimeout = (args) =>
                {
                    _output.WriteLine("OnTimeout 超时委托执行");
                    return ValueTask.CompletedTask;
                },
            };

            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(timeoutOption)
                .Build();

            var tcs = new CancellationTokenSource();
            try
            {
                await pipeline.ExecuteAsync
                (
                    //此处为不可取消的任务,忽略超时(会一直等待)
                    callback: async (innerToken) =>
                    {
                        //完成大约是2秒多,而不是超时的1秒多
                        await Task.Delay (1000*3);
                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: tcs.Token
                );
            }
            catch (OperationCanceledException ex)
            {
                _output.WriteLine($"任务取消,引发异常:{ex.Message}");
            }
            catch (TimeoutRejectedException ex)
            {
                _output.WriteLine($"超时,引发异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                _output.WriteLine($"API服务异常:{ex.Message}");
            }
            finally
            {
                tcs.TryReset();
            }
        }

        /// <summary>
        /// 可取消任务:超时
        /// 异步执行
        /// </summary>
        /// <returns></returns>
        [Fact]
        public async Task Has_CancellationToken_ExecuteAsync_Timeout_Test()
        {
            var pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromSeconds(1))
                .Build();

            await Assert.ThrowsAnyAsync<Exception>(async () => 
            {
                var cts = new CancellationTokenSource();
                await pipeline.ExecuteAsync
                (
                   callback: async (innerToken) =>
                    {
                        //完成大约是超时的1秒多,而不是任务的3秒多
                        await Task.Delay(1000 * 3, innerToken);
                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: cts.Token
                );
            });
        }

        /// <summary>
        /// 可取消任务:不超时
        /// 异步执行
        /// </summary>
        /// <returns></returns>
        [Fact]
        public async Task Has_CancellationToken_ExecuteAsync_NoTimeout_Test()
        {
            var pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromSeconds(3))
                .Build();

            try
            {
                var cts = new CancellationTokenSource();
                await pipeline.ExecuteAsync
                (
                   callback: async (innerToken) =>
                    {
                        //不超时,完成大约是任务耗时的1秒
                        await Task.Delay(1000, innerToken);
                        _output.WriteLine("任务执行完成");
                    },
                    cancellationToken: cts.Token
                );
            }
            catch (Exception ex)
            {
                _output.WriteLine(ex.Message);
            }
        }

        // <summary>
        /// 可取消任务:超时
        /// 异步执行,无外层Token
        /// </summary>
        /// <returns></returns>
        [Fact]
        public async Task Has_OuterCancellationToken_ExecuteAsync_Timeout_Test()
        {
            var pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromSeconds(1))
                .Build();


            await Assert.ThrowsAnyAsync<Exception>(async () => 
            {
                await pipeline.ExecuteAsync
                (
                   callback: async (innerToken) =>
                   {
                       //完成大约是超时的1秒多,而不是任务的3秒多
                       await Task.Delay(1000 * 3, innerToken);
                       _output.WriteLine("任务执行完成");
                   }
                );
            });
        }


        [Fact]
        public void SyncWithCancellationToken_Test()
        {
            ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
                .AddTimeout(TimeSpan.FromSeconds(3))
                .Build();

            var tcs = new CancellationTokenSource();
            try
            {
                pipeline.Execute
                (
                    //此处必须是可取消的任务,否则超时不作为(一直等待)
                    callback: SyncWithCancellationToken,
                    cancellationToken: tcs.Token
                );
            }
            catch (OperationCanceledException ex)
            {
                _output.WriteLine($"任务取消,引发异常:{ex.Message}");
            }
            catch (TimeoutRejectedException ex)
            {
                _output.WriteLine($"超时,引发异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                _output.WriteLine($"API服务异常:{ex.Message}");
            }
            finally
            {
                tcs.TryReset();
            }
        }

        /// <summary>
        /// 使用CancellationToken的同步方法
        /// </summary>
        private void SyncWithCancellationToken(CancellationToken cancellationToken)
        {
            Thread thread = new Thread((token) =>
            {
                int max = 500;
                while (max > 0)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    max -= 1;
                    Thread.Sleep(10);
                }
            });

            thread.Start();
            thread.Join();
        }
    }
}