using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.IntegrationTests.Helpers;

namespace Titanium.Web.Proxy.IntegrationTests
{
    [TestClass]
    public class ExpectContinueTests
    {
        [TestMethod]
        public async Task ReverseProxy_GotContinueAndOkResponse()
        {
            var testSuite = new TestSuite();
            var server = testSuite.GetServer();
            var continueServer = new HttpContinueServer
            {
                ExpectationResponse = HttpStatusCode.Continue,
                ResponseBody = "I am server. I received your greetings."
            };
            server.HandleTcpRequest(continueServer.HandleRequest);

            var proxy = testSuite.GetReverseProxy();
            proxy.Enable100ContinueBehaviour = true;
            proxy.BeforeRequest += (sender, e) =>
            {
                e.HttpClient.Request.RequestUri = new Uri(server.ListeningTcpUrl);
                return Task.CompletedTask;
            };

            var client = new HttpContinueClient();
            var response = await client.Post("localhost", proxy.ProxyEndPoints[0].Port, "Hello server. I am a client.");

            Assert.IsNotNull(response, "No response to 'expect: 100-continue' request");
            Assert.AreEqual((int)HttpStatusCode.OK, response.StatusCode);
            Assert.AreEqual(continueServer.ResponseBody, response.BodyString);
        }

        [TestMethod]
        public async Task ReverseProxy_GotExpectationFailedResponse()
        {
            var testSuite = new TestSuite();
            var server = testSuite.GetServer();
            var continueServer = new HttpContinueServer { ExpectationResponse = HttpStatusCode.ExpectationFailed };
            server.HandleTcpRequest(continueServer.HandleRequest);

            var proxy = testSuite.GetReverseProxy();
            proxy.Enable100ContinueBehaviour = true;
            proxy.BeforeRequest += (sender, e) =>
            {
                e.HttpClient.Request.RequestUri = new Uri(server.ListeningTcpUrl);
                return Task.CompletedTask;
            };

            var client = new HttpContinueClient();
            var response = await client.Post("localhost", proxy.ProxyEndPoints[0].Port, "Hello server. I am a client.");

            Assert.IsNotNull(response, "No response to 'expect: 100-continue' request");
            Assert.AreEqual((int)HttpStatusCode.ExpectationFailed, response.StatusCode);
        }

        [TestMethod]
        public async Task ReverseProxy_GotNotFoundResponse()
        {
            var testSuite = new TestSuite();
            var server = testSuite.GetServer();
            var continueServer = new HttpContinueServer { ExpectationResponse = HttpStatusCode.NotFound };
            server.HandleTcpRequest(continueServer.HandleRequest);

            var proxy = testSuite.GetReverseProxy();
            proxy.Enable100ContinueBehaviour = true;
            proxy.BeforeRequest += (sender, e) =>
            {
                e.HttpClient.Request.RequestUri = new Uri(server.ListeningTcpUrl);
                return Task.CompletedTask;
            };

            var client = new HttpContinueClient();
            var response = await client.Post("localhost", proxy.ProxyEndPoints[0].Port, "Hello server. I am a client.");

            Assert.IsNotNull(response, "No response to 'expect: 100-continue' request");
            Assert.AreEqual((int)HttpStatusCode.NotFound, response.StatusCode);
        }

        [TestMethod]
        public async Task ReverseProxy_BeforeRequestThrows()
        {
            var testSuite = new TestSuite();
            var server = testSuite.GetServer();
            var continueServer = new HttpContinueServer { ExpectationResponse = HttpStatusCode.Continue };
            server.HandleTcpRequest(continueServer.HandleRequest);

            var dbzEx = new DivideByZeroException("Undefined");
            var dbzString = $"{dbzEx.GetType()}: {dbzEx.Message}";

            var proxy = testSuite.GetReverseProxy();
            proxy.Enable100ContinueBehaviour = true;
            proxy.BeforeRequest += (sender, e) =>
            {
                try
                {
                    e.HttpClient.Request.RequestUri = new Uri(server.ListeningTcpUrl);
                    throw dbzEx;
                }
                catch
                {
                    var serverError = new Response(Encoding.ASCII.GetBytes(dbzString))
                    {
                        HttpVersion = new Version(1, 1),
                        StatusCode = (int)HttpStatusCode.InternalServerError,
                        StatusDescription = HttpStatusCode.InternalServerError.ToString()
                    };

                    e.Respond(serverError);
                }

                return Task.CompletedTask;
            };

            var client = new HttpContinueClient();
            var response = await client.Post("localhost", proxy.ProxyEndPoints[0].Port, "Hello server. I am a client.");

            Assert.IsNotNull(response, "No response to 'expect: 100-continue' request");
            Assert.AreEqual(response.StatusCode, (int)HttpStatusCode.InternalServerError);
            Assert.AreEqual(response.BodyString, dbzString);
        }
    }
}
