using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Text;
using System.Threading.Tasks;
namespace FastGithub.HttpServer.TcpMiddlewares
{
    /// 
    /// 正向代理中间件
    /// 
    sealed class HttpProxyMiddleware
    {
        private readonly HttpParser httpParser = new();
        private readonly byte[] http200 = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n");
        private readonly byte[] http400 = Encoding.ASCII.GetBytes("HTTP/1.1 400 Bad Request\r\n\r\n");
        /// 
        /// 执行中间件
        /// 
        /// 
        /// 
        /// 
        public async Task InvokeAsync(ConnectionDelegate next, ConnectionContext context)
        {
            var result = await context.Transport.Input.ReadAsync();
            var httpRequest = this.GetHttpRequestHandler(result, out var consumed);
            // 协议错误
            if (consumed == 0L)
            {
                await context.Transport.Output.WriteAsync(this.http400, context.ConnectionClosed);
            }
            else
            {
                // 隧道代理连接请求
                if (httpRequest.ProxyProtocol == ProxyProtocol.TunnelProxy)
                {
                    var position = result.Buffer.GetPosition(consumed);
                    context.Transport.Input.AdvanceTo(position);
                    await context.Transport.Output.WriteAsync(this.http200, context.ConnectionClosed);
                }
                else
                {
                    var position = result.Buffer.Start;
                    context.Transport.Input.AdvanceTo(position);
                }
                context.Features.Set(httpRequest);
                await next(context);
            }
        }
        /// 
        /// 获取http请求处理者
        /// 
        /// 
        /// 
        /// 
        private HttpRequestHandler GetHttpRequestHandler(ReadResult result, out long consumed)
        {
            var handler = new HttpRequestHandler();
            var reader = new SequenceReader(result.Buffer);
            if (this.httpParser.ParseRequestLine(handler, ref reader) &&
                this.httpParser.ParseHeaders(handler, ref reader))
            {
                consumed = reader.Consumed;
            }
            else
            {
                consumed = 0L;
            }
            return handler;
        }
        /// 
        /// 代理请求处理器
        /// 
        private class HttpRequestHandler : IHttpRequestLineHandler, IHttpHeadersHandler, IHttpProxyFeature
        {
            private HttpMethod method;
            public HostString ProxyHost { get; private set; }
            public ProxyProtocol ProxyProtocol
            {
                get
                {
                    if (this.ProxyHost.HasValue == false)
                    {
                        return ProxyProtocol.None;
                    }
                    if (this.method == HttpMethod.Connect)
                    {
                        return ProxyProtocol.TunnelProxy;
                    }
                    return ProxyProtocol.HttpProxy;
                }
            }
            void IHttpRequestLineHandler.OnStartLine(HttpVersionAndMethod versionAndMethod, TargetOffsetPathLength targetPath, Span startLine)
            {
                this.method = versionAndMethod.Method;
                var host = Encoding.ASCII.GetString(startLine.Slice(targetPath.Offset, targetPath.Length));
                if (versionAndMethod.Method == HttpMethod.Connect)
                {
                    this.ProxyHost = HostString.FromUriComponent(host);
                }
                else if (Uri.TryCreate(host, UriKind.Absolute, out var uri))
                {
                    this.ProxyHost = HostString.FromUriComponent(uri);
                }
            }
            void IHttpHeadersHandler.OnHeader(ReadOnlySpan name, ReadOnlySpan value)
            {
            }
            void IHttpHeadersHandler.OnHeadersComplete(bool endStream)
            {
            }
            void IHttpHeadersHandler.OnStaticIndexedHeader(int index)
            {
            }
            void IHttpHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan value)
            {
            }
        }
    }
}