using FastGithub.DomainResolve;
using Microsoft.AspNetCore.Connections;
using System;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.ReverseProxy
{
    /// 
    /// github的ssl处理者
    /// 
    sealed class GitubSshHandler : ConnectionHandler
    {
        private const int SSH_PORT = 22;
        private const string GITHUB_COM = "github.com";
        private readonly IDomainResolver domainResolver;
        /// 
        /// github的ssl处理者
        /// 
        /// 
        public GitubSshHandler(IDomainResolver domainResolver)
        {
            this.domainResolver = domainResolver;
        }
        /// 
        /// ssh连接后
        /// 
        /// 
        /// 
        public override async Task OnConnectedAsync(ConnectionContext connection)
        {
            var address = await this.domainResolver.ResolveAsync(GITHUB_COM, CancellationToken.None);
            var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            await socket.ConnectAsync(new IPEndPoint(address, SSH_PORT));
            using var upStream = new NetworkStream(socket, ownsSocket: true);
            var downStream = new SshStream(connection.Transport);
            var task1 = upStream.CopyToAsync(downStream);
            var task2 = downStream.CopyToAsync(upStream);
            await Task.WhenAny(task1, task2);
        }
        /// 
        /// 表示Ssh的流
        /// 
        private class SshStream : Stream
        {
            private readonly Stream readStream;
            private readonly Stream wirteStream;
            /// 
            /// Ssh的流
            /// 
            /// 
            public SshStream(IDuplexPipe transport)
            {
                this.readStream = transport.Input.AsStream();
                this.wirteStream = transport.Output.AsStream();
            }
            public override bool CanRead => true;
            public override bool CanSeek => false;
            public override bool CanWrite => true;
            public override long Length => throw new NotSupportedException();
            public override long Position
            {
                get => throw new NotSupportedException();
                set => throw new NotSupportedException();
            }
            public override void Flush()
            {
                this.wirteStream.Flush();
            }
            public override Task FlushAsync(CancellationToken cancellationToken)
            {
                return this.wirteStream.FlushAsync(cancellationToken);
            }
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw new NotSupportedException();
            }
            public override void SetLength(long value)
            {
                throw new NotSupportedException();
            }
            public override int Read(byte[] buffer, int offset, int count)
            {
                return this.readStream.Read(buffer, offset, count);
            }
            public override void Write(byte[] buffer, int offset, int count)
            {
                this.wirteStream.Write(buffer, offset, count);
            }
            public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
            {
                return this.readStream.ReadAsync(buffer, cancellationToken);
            }
            public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                return this.readStream.ReadAsync(buffer, offset, count, cancellationToken);
            }
            public override void Write(ReadOnlySpan buffer)
            {
                this.wirteStream.Write(buffer);
            }
            public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken);
            }
            public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
            {
                await this.wirteStream.WriteAsync(buffer, cancellationToken);
            }
        }
    }
}