namespace SharpChat.Snowflake;

public class SnowflakeGenerator {
    public const long MASK = 0x7FFFFFFFFFFFFFFF;
    // previous default epoch was 1588377600000, but snowflakes are much larger than SharpIds
    public const long EPOCH = 1356998400000;
    public const byte SHIFT = 16;

    public readonly long Epoch;
    public readonly byte Shift;
    public readonly long TimestampMask;
    public readonly long SequenceMask;

    public SnowflakeGenerator(long epoch = EPOCH, byte shift = SHIFT) {
        if(epoch is < 0 or > MASK)
            throw new ArgumentException("Epoch must be a positive int64.", nameof(epoch));
        if(shift is < 1 or > 63)
            throw new ArgumentException("Shift must be between or equal to 1 and 63", nameof(shift));

        Epoch = epoch;
        Shift = shift;

        // i think Index only does this as a hack for how integers work in PHP but its gonna run Once per application instance lol
        TimestampMask = ~(~0L << (63 - shift));
        SequenceMask = ~(~0L << shift);
    }

    public long Now(DateTimeOffset? at = null) {
        return Math.Max(0, (at ?? DateTimeOffset.UtcNow).ToUnixTimeMilliseconds() - Epoch);
    }

    public long Next(long sequence, DateTimeOffset? at = null) {
        return ((Now(at) & TimestampMask) << Shift) | (sequence & SequenceMask);
    }
}