namespace Ocelot.AcceptanceTests
{
    using Microsoft.AspNetCore.Http;
    using Ocelot.Configuration.File;
    using Shouldly;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.Compression;
    using System.Linq;
    using System.Net;
    using TestStack.BDDfy;
    using Xunit;

    public class GzipTests : IDisposable
    {
        private readonly Steps _steps;
        private readonly ServiceHandler _serviceHandler;

        public GzipTests()
        {
            _serviceHandler = new ServiceHandler();
            _steps = new Steps();
        }

        [Fact]
        public void should_return_response_200_with_simple_url()
        {
            var configuration = new FileConfiguration
            {
                ReRoutes = new List<FileReRoute>
                    {
                        new FileReRoute
                        {
                            DownstreamPathTemplate = "/",
                            DownstreamScheme = "http",
                            DownstreamHostAndPorts = new List<FileHostAndPort>
                            {
                                new FileHostAndPort
                                {
                                    Host = "localhost",
                                    Port = 51179,
                                }
                            },
                            UpstreamPathTemplate = "/",
                            UpstreamHttpMethod = new List<string> { "Post" },
                        }
                    }
            };

            var input = "people";

            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51179", "/", 200, "Hello from Laura", "\"people\""))
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
                .And(x => _steps.GivenOcelotIsRunning())
                .And(x => _steps.GivenThePostHasGzipContent(input))
                .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
                .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
                .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
                .BDDfy();
        }

        private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expected)
        {
            _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
            {
                if (context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding))
                {
                    contentEncoding.First().ShouldBe("gzip");

                    string text = null;
                    using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress))
                    {
                        using (var sr = new StreamReader(decompress))
                        {
                            // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
                            // text = sr.ReadToEnd();
                            text = await sr.ReadToEndAsync();
                        }
                    }

                    if (text != expected)
                    {
                        throw new Exception("not gzipped");
                    }

                    context.Response.StatusCode = statusCode;
                    await context.Response.WriteAsync(responseBody);
                }
                else
                {
                    context.Response.StatusCode = statusCode;
                    await context.Response.WriteAsync("downstream path didnt match base path");
                }
            });
        }

        public void Dispose()
        {
            _serviceHandler?.Dispose();
            _steps.Dispose();
        }
    }
}