using Fleck;
using System.Net;

namespace SharpChat {
    public class ChatConnection : IDisposable {
        public const int ID_LENGTH = 20;

#if DEBUG
        public static TimeSpan SessionTimeOut { get; } = TimeSpan.FromMinutes(1);
#else
        public static TimeSpan SessionTimeOut { get; } = TimeSpan.FromMinutes(5);
#endif

        public IWebSocketConnection Socket { get; }

        public string Id { get; }
        public bool IsDisposed { get; private set; }
        public DateTimeOffset LastPing { get; set; } = DateTimeOffset.Now;
        public ChatUser? User { get; set; }

        private int CloseCode { get; set; } = 1000;

        public IPAddress RemoteAddress { get; }
        public ushort RemotePort { get; }

        public bool IsAlive => !IsDisposed && !HasTimedOut;

        public ChatConnection(IWebSocketConnection sock) {
            Socket = sock;
            Id = RNG.SecureRandomString(ID_LENGTH);

            if(!IPAddress.TryParse(sock.ConnectionInfo.ClientIpAddress, out IPAddress? addr))
                throw new Exception("Unable to parse remote address?????");

            if(IPAddress.IsLoopback(addr)
                && sock.ConnectionInfo.Headers.TryGetValue("X-Real-IP", out string? addrStr)
                && IPAddress.TryParse(addrStr, out IPAddress? realAddr))
                addr = realAddr;

            RemoteAddress = addr;
            RemotePort = (ushort)sock.ConnectionInfo.ClientPort;
        }

        public void Send(S2CPacket packet) {
            if(!Socket.IsAvailable)
                return;

            IEnumerable<string> data = packet.Pack();

            if(data != null)
                foreach(string line in data)
                    if(!string.IsNullOrWhiteSpace(line))
                        Socket.Send(line);
        }

        public void BumpPing() {
            LastPing = DateTimeOffset.Now;
        }

        public bool HasTimedOut
            => DateTimeOffset.Now - LastPing > SessionTimeOut;

        public void PrepareForRestart() {
            CloseCode = 1012;
        }

        ~ChatConnection() {
            DoDispose();
        }

        public void Dispose() {
            DoDispose();
            GC.SuppressFinalize(this);
        }

        private void DoDispose() {
            if(IsDisposed)
                return;

            IsDisposed = true;
            Socket.Close(CloseCode);
        }

        public override string ToString() {
            return Id;
        }

        public override int GetHashCode() {
            return Id.GetHashCode();
        }
    }
}