using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Ultron.Proxy.Utils;
using Ultron.Proxy.Interfaces;

namespace Ultron.Proxy
{
    //+------------------------+
    //| NAT                    |
    //|                        |
    //|                        |
    //|    +----------+        |   +-----------+
    //|    |          |        |   |           |
    //|    |  client  |------------>  provider |
    //|    |          |        |   |           |
    //|    +----+-----+        |   +------^----+
    //|         |              |          |
    //|         |              |          |
    //|         |              |          |
    //|    +----V-----+        |          |
    //|    |          |        |          |
    //|    |   IIS    |        |          |
    //|    |          |        |          |
    //|    +----------+        |   +------+-------+
    //|                        |   |              |
    //|                        |   |   consumer   |
    //|                        |   |              |
    //+------------------------+   +--------------+
    public class ServerHost
    {
        //服务端代理转发端口
        public static int ClientServicePort = 9973;
        //服务端配置通讯端口
        public static int ConfigServicePort = 12307;
        //远端管理端口
        public static int WebManagementPort = 0;

        public ClientConnectionManager ConnectionManager = null;

        //inject
        internal static ILogger Logger;

        public ServerHost(ILogger logger)
        {
            Logger = logger;
        }

        //必须设置远程端口才可以通信
        public ServerHost SetWebPort(int port)
        {
            WebManagementPort = port;
            return this;
        }

        public async Task Start()
        {
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            CancellationTokenSource ctsConfig = new CancellationTokenSource();
            CancellationTokenSource ctsHttp = new CancellationTokenSource();
            CancellationTokenSource ctsConsumer = new CancellationTokenSource();

            //1.反向连接池配置
            ConnectionManager = ClientConnectionManager.GetInstance();
            //注册客户端发生连接时的事件
            ConnectionManager.AppTcpClientMapConfigConnected += ConnectionManager_AppAdded;
            Logger.Debug("Ultron.Proxy server started");

            //2.开启http服务
            if (WebManagementPort > 0)
                StartHttpService(ctsHttp);

            //3.开启配置服务

            try
            {
                await StartConfigService(ctsConfig);
            }
            catch (Exception ex)
            {
                Logger.Debug(ex.Message);
            }
            finally
            {
                Logger.Debug("all closed");
                ctsConfig.Cancel();
                //listenerConsumer.Stop();
            }
            ////4.通过已配置的端口集合开启侦听
            //foreach (var kv in ConnectionManager.PortAppMap)
            //{
            //    ListenConsumeAsync(kv.Key, ctsConsumer.Token);
            //}

        }

        private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            Logger.Error(e.Exception.ToString(), e.Exception);
        }

        #region HTTPServer
        private async Task StartHttpService(CancellationTokenSource ctsHttp)
        {
            try
            {
                HttpListener listener = new HttpListener();
                listener.Prefixes.Add($"http://127.0.0.1:{WebManagementPort}/");
                //TcpListener listenerConfigService = new TcpListener(IPAddress.Any, WebManagementPort);
                Logger.Debug("Listening HTTP request on port " + WebManagementPort.ToString() + "...");
                await AcceptHttpRequest(listener, ctsHttp);
            }
            catch (HttpListenerException ex)
            {
                Logger.Debug("Please run this program in administrator mode." + ex);
                ServerHost.Logger.Error(ex.ToString(), ex);
            }
            catch (Exception ex)
            {
                Logger.Debug(ex);
                ServerHost.Logger.Error(ex.ToString(), ex);
            }
        }

        private async Task AcceptHttpRequest(HttpListener httpService, CancellationTokenSource ctsHttp)
        {
            httpService.Start();
            while (true)
            {
                var client = await httpService.GetContextAsync();
                ProcessHttpRequestAsync(client);
            }
        }

        private async Task ProcessHttpRequestAsync(HttpListenerContext context)
        {
            try
            {


                var request = context.Request;
                var response = context.Response;

                response.ContentEncoding = Encoding.UTF8;
                response.ContentType = "text/html;charset=utf-8";

                //getJson
                StringBuilder json = new StringBuilder("[ ");
                foreach (var app in this.ConnectionManager.PortAppMap)
                {
                    json.Append("{ ");
                    json.Append(KV2Json("port", app.Key)).C();
                    json.Append(KV2Json("clientId", app.Value.ClientIdAppId.ClientID)).C();
                    json.Append(KV2Json("appId", app.Value.ClientIdAppId.AppID)).C();

                    //反向连接
                    json.Append(KV2Json("revconns"));
                    json.Append("[ ");
                    foreach (var reverseClient in app.Value.ReverseClients)
                    {
                        json.Append("{ ");
                        if (reverseClient.Connected)
                        {
                            json.Append(KV2Json("lEndPoint", reverseClient.Client.LocalEndPoint.ToString())).C();
                            json.Append(KV2Json("rEndPoint", reverseClient.Client.RemoteEndPoint.ToString()));
                        }

                        //json.Append(KV2Json("p", c)).C();
                        //json.Append(KV2Json("port", ca.Key));
                        json.Append("}");
                        json.C();
                    }
                    json.D();
                    json.Append("]").C(); ;

                    //隧道状态
                    json.Append(KV2Json("tunnels"));
                    json.Append("[ ");
                    foreach (var tunnel in app.Value.Tunnels)
                    {
                        json.Append("{ ");
                        if (tunnel.ClientServerClient.Connected)

                            json.Append(KV2Json("clientServerClient", tunnel.ClientServerClient?.Client.LocalEndPoint.ToString())).C();
                        if (tunnel.ConsumerClient.Connected)
                            json.Append(KV2Json("consumerClient", tunnel.ConsumerClient?.Client.LocalEndPoint.ToString())).C();

                        json.D();
                        //json.Append(KV2Json("p", c)).C();
                        //json.Append(KV2Json("port", ca.Key));
                        json.Append("}");
                        json.C();
                    }
                    json.D();
                    json.Append("]");
                    json.Append("}").C();
                }
                json.D();
                json.Append("]");
                await response.OutputStream.WriteAsync(HtmlUtil.GetContent(json.ToString()));
                //await response.OutputStream.WriteAsync(HtmlUtil.GetContent(request.RawUrl));
                response.OutputStream.Close();
            }
            catch (Exception e)
            {
                Logger.Error(e.Message, e);
                throw;
            }
        }

        private string KV2Json(string key)
        {
            return "\"" + key + "\":";
        }
        private string KV2Json(string key, object value)
        {
            return "\"" + key + "\":\"" + value.ToString() + "\"";
        }

        #endregion


        private async Task StartConfigService(CancellationTokenSource accepting)
        {
            TcpListener listenerConfigService = new TcpListener(IPAddress.Any, ConfigServicePort);


            Logger.Debug("Listening config request on port " + ConfigServicePort.ToString() + "...");
            var taskResultConfig = AcceptConfigRequest(listenerConfigService);

            await taskResultConfig; //block here to hold open the server

        }

        /// <summary>
        /// 有连接连上则开始侦听新的端口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ConnectionManager_AppAdded(object sender, AppChangedEventArgs e)
        {
            Logger.Debug("AppTcpClientMapReverseConnected事件已触发");
            int port = 0;
            foreach (var kv in ConnectionManager.PortAppMap)
            {
                if (kv.Value.ClientIdAppId.AppID == e.App.AppID &&
                    kv.Value.ClientIdAppId.ClientID == e.App.ClientID) port = kv.Key;
            }
            if (port == 0) throw new Exception("app未注册");
            var ct = new CancellationToken();


            ListenConsumeAsync(port, ct);
        }

        #region 配置
        //配置服务，客户端可以通过这个服务接收现有的空闲端口
        //accept a config request.
        //request:
        //   2          1       1
        //  clientid    appid   nouse
        //
        //response:
        //   2          1       1  ...N
        //  clientid    appid   port
        private async Task AcceptConfigRequest(TcpListener listenerConfigService)
        {
            listenerConfigService.Start(100);
            while (true)
            {
                var client = await listenerConfigService.AcceptTcpClientAsync();
                ProcessConfigRequestAsync(client);
            }
        }

        private async Task ProcessConfigRequestAsync(TcpClient client)
        {
            try
            {
                //长度固定4个字节
                int configRequestLength = 3;
                byte[] appRequestBytes = new byte[configRequestLength];
                Logger.Debug("config request received.");
                var nstream = client.GetStream();

                //1.读取配置请求1
                int resultByte = await nstream.ReadAsync(appRequestBytes);
                Logger.Debug("appRequestBytes received.");
                if (resultByte == 0)
                {
                    CloseClient(client);
                    return;
                }
                //2.根据配置请求1获取更多配置信息
                int appCount = (int)appRequestBytes[2];
                byte[] consumerPortBytes = new byte[appCount * 2];
                int resultByte2 = await nstream.ReadAsync(consumerPortBytes);
                Logger.Debug("consumerPortBytes received.");
                if (resultByte2 == 0)
                {
                    CloseClient(client);
                    return;
                }

                //3.分配配置ID，并且写回给客户端
                try
                {
                    byte[] arrangedIds = ConnectionManager.ArrageConfigIds(appRequestBytes, consumerPortBytes);
                    Logger.Debug("apprequest arranged");
                    await nstream.WriteAsync(arrangedIds);
                }
                catch (Exception ex)
                { Logger.Debug(ex.ToString()); }


                Logger.Debug("arrangedIds written.");
            }
            catch (Exception e)
            {
                Logger.Debug(e);
                throw;
            }

        }
        #endregion


        /// <summary>
        /// 同时侦听来自consumer的链接和到provider的链接
        /// </summary>
        /// <param name="consumerlistener"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        async Task ListenConsumeAsync(int consumerPort, CancellationToken ct)
        {
            try
            {

                var consumerlistener = new TcpListener(IPAddress.Any, consumerPort);
                consumerlistener.Start(1000);
                //给两个listen，同时监听3端
                var clientCounter = 0;
                while (!ct.IsCancellationRequested)
                {
                    //目标的代理服务联通了，才去处理consumer端的请求。
                    Logger.Debug("listening serviceClient....Port:" + consumerPort);
                    TcpClient consumerClient = await consumerlistener.AcceptTcpClientAsync();
                    //记录tcp隧道，消费端
                    TcpTunnel tunnel = new TcpTunnel();
                    tunnel.ConsumerClient = consumerClient;
                    ClientConnectionManager.GetInstance().PortAppMap[consumerPort].Tunnels.Add(tunnel);
                    Logger.Debug("consumer已连接：" + consumerClient.Client.RemoteEndPoint.ToString());
                    //消费端连接成功,连接


                    //需要端口
                    TcpClient s2pClient = await ConnectionManager.GetClient(consumerPort);
                    //记录tcp隧道，客户端
                    tunnel.ClientServerClient = s2pClient;
                    //✳关键过程✳
                    //连接完之后发送一个字节过去促使客户端建立转发隧道
                    await s2pClient.GetStream().WriteAsync(new byte[] { 1 }, 0, 1);
                    clientCounter++;

                    TcpTransferAsync(consumerlistener, consumerClient, s2pClient, clientCounter, ct);
                }
            }
            catch (Exception e)
            {
                Logger.Debug(e);
            }

        }

        #region datatransfer
        //3端互相传输数据
        async Task TcpTransferAsync(TcpListener consumerlistener, TcpClient consumerClient, TcpClient providerClient,
            int clientIndex,
            CancellationToken ct)
        {
            try
            {
                ServerHost.Logger.Debug($"New client ({clientIndex}) connected");

                CancellationTokenSource transfering = new CancellationTokenSource();

                var providerStream = providerClient.GetStream();
                var consumerStream = consumerClient.GetStream();
                Task taskC2PLooping = ToStaticTransfer(transfering.Token, consumerStream, providerStream);
                Task taskP2CLooping = StreamTransfer(transfering.Token, providerStream, consumerStream);

                //任何一端传输中断或者故障，则关闭所有连接
                var comletedTask = await Task.WhenAny(taskC2PLooping, taskP2CLooping);
                //comletedTask.
                Logger.Debug($"Transferring ({clientIndex}) STOPPED");
                consumerClient.Close();
                providerClient.Close();
                transfering.Cancel();
            }
            catch (Exception e)
            {
                Logger.Debug(e);
                throw;
            }

        }

        private async Task StreamTransfer(CancellationToken ct, NetworkStream fromStream, NetworkStream toStream)
        {
            await fromStream.CopyToAsync(toStream, ct);
        }

        private async Task ToStaticTransfer(CancellationToken ct, NetworkStream fromStream, NetworkStream toStream, Func<byte[], Task<bool>> beforeTransferHandle = null)
        {
            await fromStream.CopyToAsync(toStream, ct);
        }

        private void CloseClient(TcpClient client)
        {
            Logger.Debug("invalid request,Closing client:" + client.Client.RemoteEndPoint.ToString());
            client.Close();
            Logger.Debug("Closed client:" + client.Client.RemoteEndPoint.ToString());
        }



        #endregion


    }
}