using Fleck;
using SharpChat.SockChat;
using System.Net;

namespace SharpChat;

public class Connection : 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 User? User { get; set; }

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

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

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

    public Connection(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 async Task Send(S2CPacket packet) {
        if(!Socket.IsAvailable)
            return;

        string data = packet.Pack();
        if(!string.IsNullOrWhiteSpace(data))
            await Socket.Send(data);
    }

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

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

    public void PrepareForRestart() {
        CloseCode = 1012;
    }

    ~Connection() {
        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();
    }
}