using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Logging;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Fody;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Team.Communicate.EventArg;
using Team.Communicate.Handlers;

namespace Team.Communicate.Server
{
    // ReSharper disable once ClassNeverInstantiated.Global
    public class DotnettyServer : AbstractTcpConnector, IDisposable
    {
        private readonly ServerBootstrap _bootstrap = new();
        private readonly MultithreadEventLoopGroup _bossGroup = new(1);
        private readonly MultithreadEventLoopGroup _workerGroup = new();
        private IChannel _bootstrapChannel;

        private IChannelPipeline _channelPipeline;

        public ConcurrentDictionary<int, TcpStateEventArgs> ConnectionClients { get; }
        private bool _isInit;

        private readonly List<string> UseUngisterKeys = new();
        public event EventHandler<TcpStateEventArgs> ConnectionEvent;
        public event EventHandler<UnRegisterEventArgs> UnRegisterMessageEvent;
        private readonly TelnetServerHandler _serverHandler;
        public override Encoding Encoding
        {
            get => base.Encoding;
            set
            {
                base.Encoding = value;
                _serverHandler?.SetEncoding(value);
            }
        }
        public DotnettyServer()
        {
            ConnectionClients = new ConcurrentDictionary<int, TcpStateEventArgs>();
            Environment.SetEnvironmentVariable("io.netty.allocator.numDirectArenas", "0");
            _serverHandler = new TelnetServerHandler
            {
                Encoding = Encoding
            };

        }
        public override void SetEnd(string endWith)
        {
            base.SetEnd(endWith);
            _serverHandler.SetEndWith(endWith);
        }
        public override void UseUnRegisterMessage(string key)
        {
            if (UseUngisterKeys.Contains(key))
            {
                return;
            }
            UseUngisterKeys.Add(key);
            _serverHandler.SetUseUnRegisterKeys(UseUngisterKeys);
        }

        public override void Register(string id, Func<string, IChannelHandlerContext, ValueTask<string>> func)
        {
            _serverHandler.Register(id, func);
        }

        public override void UnRegister(string id)
        {
            _serverHandler.UnRegister(id);
        }

        public void ClearAllRegister()
        {
            _serverHandler.ClearAllRegister();
        }

        public void SetEventHandler(Func<string, IChannelHandlerContext, Task<string>> sendEventHandler)
        {
        }


        [ConfigureAwait(false)]
        public async Task<bool> StartAsync(int port)
        {
            if (_isInit)
            {
                return true;
            }

            var stringEncoder = new StringEncoder(Encoding);
            var stringDecoder = new StringDecoder(Encoding);
            _bootstrap
                .Group(_bossGroup, _workerGroup)
                .Channel<TcpServerSocketChannel>()
                .Option(ChannelOption.TcpNodelay, true)
                .Option(ChannelOption.SoBacklog, 100)
                .Handler(new LoggingHandler(LogLevel.INFO))
                .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    var pipeline = channel.Pipeline;
                    _channelPipeline = pipeline;
                    pipeline.AddLast(new DelimiterBasedFrameDecoder(8192, ByteBuffers));
                    //pipeline.AddLast(stringEncoder, stringDecoder, _serverHandler);
                    pipeline.AddLast("echo", _serverHandler);
                }));
            _serverHandler.ConnectionEvent += ServerHandler_ConnectionEvent;
            _serverHandler.UseUnRegisterAction += (s, e) =>
            {

                UnRegisterMessageEvent?.Invoke(this, new UnRegisterEventArgs(new UnRegisterContent(s, e, Encoding)));
            };
            try
            {
                _bootstrapChannel = await _bootstrap.BindAsync(port);
                _isInit = true;
                return _isInit;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

        }

        private void ServerHandler_ConnectionEvent(object sender, TcpStateEventArgs e)
        {
            var id = e.ServerConnection.GetHashCode();
            if (e.ServerConnection.Connected)
            {
                if (!ConnectionClients.ContainsKey(id))
                {
                    ConnectionClients.TryAdd(id, e);
                }
                else
                {
                    ConnectionClients[id] = e;
                }

            }
            else
            {
                if (ConnectionClients.ContainsKey(id))
                {
                    ConnectionClients.TryRemove(id, out _);
                }

            }

            ConnectionEvent?.Invoke(this, e);
        }
        [ConfigureAwait(false)]
        public async Task SendMessage(string msg)
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.Append(msg);
            stringBuilder.Append(_serverHandler._endWith);
            var bytes = Encoding.GetBytes(stringBuilder.ToString());
            var sendBytes = Unpooled.Buffer(256);
            sendBytes.WriteBytes(bytes);

            await _channelPipeline.WriteAndFlushAsync(sendBytes);
            //await _channelPipeline.WriteAndFlushAsync(msg + _serverHandler._endWith);

        }

        public async void Dispose()
        {
            try
            {
                if (_bootstrapChannel != null)
                {
                    await _bootstrapChannel.Pipeline.DisconnectAsync();
                    await _bootstrapChannel.Pipeline.CloseAsync();
                    await _bootstrapChannel.DisconnectAsync();
                    await _bootstrapChannel?.CloseAsync();
                }

                await _bossGroup.ShutdownGracefullyAsync();
                await _workerGroup.ShutdownGracefullyAsync();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            _isInit = false;

        }


    }
}