using FastGithub.Configuration;
using FastGithub.DomainResolve;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using System.IO.Pipelines;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Forwarder;
namespace FastGithub.HttpServer
{
    /// 
    /// http代理中间件
    /// 
    sealed class HttpProxyMiddleware
    {
        private const string LOOPBACK = "127.0.0.1";
        private const string LOCALHOST = "localhost";
        private readonly FastGithubConfig fastGithubConfig;
        private readonly IDomainResolver domainResolver;
        private readonly IHttpForwarder httpForwarder;
        private readonly HttpReverseProxyMiddleware httpReverseProxy;
        private readonly HttpMessageInvoker defaultHttpClient;
        /// 
        /// http代理中间件
        /// 
        /// 
        /// 
        /// 
        /// 
        public HttpProxyMiddleware(
            FastGithubConfig fastGithubConfig,
            IDomainResolver domainResolver,
            IHttpForwarder httpForwarder,
            HttpReverseProxyMiddleware httpReverseProxy)
        {
            this.fastGithubConfig = fastGithubConfig;
            this.domainResolver = domainResolver;
            this.httpForwarder = httpForwarder;
            this.httpReverseProxy = httpReverseProxy;
            this.defaultHttpClient = new HttpMessageInvoker(CreateDefaultHttpHandler(), disposeHandler: false);
        }
        /// 
        /// 处理请求
        /// 
        /// 
        /// 
        public async Task InvokeAsync(HttpContext context)
        {
            var host = context.Request.Host;
            if (this.IsFastGithubServer(host) == true)
            {
                var proxyPac = this.CreateProxyPac(host);
                context.Response.ContentType = "application/x-ns-proxy-autoconfig";
                context.Response.Headers.Add("Content-Disposition", $"attachment;filename=proxy.pac");
                await context.Response.WriteAsync(proxyPac);
            }
            else if (context.Request.Method == HttpMethods.Connect)
            {
                var endpoint = await this.GetTargetEndPointAsync(host);
                using var targetSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                await targetSocket.ConnectAsync(endpoint);
                context.Response.StatusCode = StatusCodes.Status200OK;
                context.Features.Get().ReasonPhrase = "Connection Established";
                await context.Response.CompleteAsync();
                var transport = context.Features.Get()?.Transport;
                if (transport != null)
                {
                    var targetStream = new NetworkStream(targetSocket, ownsSocket: false);
                    await Task.WhenAny(targetStream.CopyToAsync(transport.Output), transport.Input.CopyToAsync(targetStream));
                }
            }
            else
            {
                await this.httpReverseProxy.InvokeAsync(context, async ctx =>
                {
                    var destinationPrefix = $"{ctx.Request.Scheme}://{ctx.Request.Host}";
                    await this.httpForwarder.SendAsync(ctx, destinationPrefix, this.defaultHttpClient);
                });
            }
        }
        /// 
        /// 是否为fastgithub服务
        /// 
        /// 
        /// 
        private bool IsFastGithubServer(HostString host)
        {
            if (host.Port == this.fastGithubConfig.HttpProxyPort)
            {
                return host.Host == LOOPBACK || host.Host == LOCALHOST;
            }
            return false;
        }
        /// 
        /// 创建proxypac脚本
        /// 
        /// 
        /// 
        private string CreateProxyPac(HostString proxyHost)
        {
            var buidler = new StringBuilder();
            buidler.AppendLine("function FindProxyForURL(url, host){");
            buidler.AppendLine($"    var fastgithub = 'PROXY {proxyHost}';");
            foreach (var domain in this.fastGithubConfig.GetDomainPatterns())
            {
                buidler.AppendLine($"    if (shExpMatch(host, '{domain}')) return fastgithub;");
            }
            buidler.AppendLine("    return 'DIRECT';");
            buidler.AppendLine("}");
            return buidler.ToString();
        }
        /// 
        /// 获取目标终节点
        /// 
        /// 
        /// 
        private async Task GetTargetEndPointAsync(HostString host)
        {
            const int HTTP_PORT = 80;
            const int HTTPS_PORT = 443;
            var targetHost = host.Host;
            var targetPort = host.Port ?? HTTPS_PORT;
            if (IPAddress.TryParse(targetHost, out var address) == true)
            {
                return new IPEndPoint(address, targetPort);
            }
            // 不关心的域名,直接使用系统dns
            if (this.fastGithubConfig.IsMatch(targetHost) == false)
            {
                return new DnsEndPoint(targetHost, targetPort);
            }
            if (targetPort == HTTP_PORT)
            {
                return new IPEndPoint(IPAddress.Loopback, ReverseProxyPort.Http);
            }
            if (targetPort == HTTPS_PORT)
            {
                return new IPEndPoint(IPAddress.Loopback, ReverseProxyPort.Https);
            }
            // 不使用系统dns
            address = await this.domainResolver.ResolveAnyAsync(new DnsEndPoint(targetHost, targetPort));
            return new IPEndPoint(address, targetPort);
        }
        /// 
        /// 创建httpHandler
        /// 
        /// 
        private static SocketsHttpHandler CreateDefaultHttpHandler()
        {
            return new()
            {
                Proxy = null,
                UseProxy = false,
                UseCookies = false,
                AllowAutoRedirect = false,
                AutomaticDecompression = DecompressionMethods.None
            };
        }
    }
}