<?php
// SnowflakeTest.php
// Created: 2025-03-26
// Updated: 2025-03-26

declare(strict_types=1);

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\{CoversClass,UsesClass};
use Index\XDateTime;
use Index\Snowflake\{BinarySnowflake,IncrementalSnowflake,RandomSnowflake,SnowflakeGenerator};
use Random\Engine\Mt19937;

#[CoversClass(IncrementalSnowflake::class)]
#[CoversClass(SnowflakeGenerator::class)]
#[UsesClass(XDateTime::class)]
final class SnowflakeTest extends TestCase {
    public function testSnowflakeGenerator(): void {
        // default epoch and shift
        $generator = new SnowflakeGenerator;
        $this->assertSame(SnowflakeGenerator::EPOCH, $generator->epoch);
        $this->assertSame(SnowflakeGenerator::SHIFT, $generator->shift);
        $this->assertSame(0x7FFFFFFFFFFF, $generator->tsMask);
        $this->assertSame(0xFFFF, $generator->sqMask);
        $snowflake = $generator->next(0x123456, 1742946997548);
        $this->assertSame(0x59DC543D2C3456, $snowflake);

        // copy date exactly
        $generator = new SnowflakeGenerator(epoch: 0);
        $this->assertSame(0, $generator->epoch);
        $this->assertSame(SnowflakeGenerator::SHIFT, $generator->shift);
        $this->assertSame(0x7FFFFFFFFFFF, $generator->tsMask);
        $this->assertSame(0xFFFF, $generator->sqMask);
        $snowflake = $generator->next(0x654321, 1742946997548);
        $this->assertSame(0x195CFBC952C4321, $snowflake);

        // quirky shift
        $generator = new SnowflakeGenerator(shift: 14);
        $this->assertSame(SnowflakeGenerator::EPOCH, $generator->epoch);
        $this->assertSame(14, $generator->shift);
        $this->assertSame(0x1FFFFFFFFFFFF, $generator->tsMask);
        $this->assertSame(0x3FFF, $generator->sqMask);
        $snowflake = $generator->next(0xFDE9, 1742947403098);
        $this->assertSame(0x1677169B56BDE9, $snowflake);

        // incremental
        $incremental = new IncrementalSnowflake(0x10001, $generator);
        $this->assertSame(0x10002, $incremental->increment());
        $this->assertSame(0x16771F5C1C4003, $incremental->next(1742949697649));

        // big endian binary
        $big = new BinarySnowflake(generator: new SnowflakeGenerator(shift: 20));
        $this->assertSame(0x59DC920D23000DE, $big->next("\xDE", 1742951048483));
        $this->assertSame(0x59DC920D230DEAD, $big->next("\xDE\xAD", 1742951048483));
        $this->assertSame(0x59DC920D23EADBE, $big->next("\xDE\xAD\xBE", 1742951048483));
        $this->assertSame(0x59DC920D23FBEAD, $big->next("\xDE\xAF\xBE\xAD", 1742951048483));

        // little endian binary
        $little = new BinarySnowflake(littleEndian: true, generator: new SnowflakeGenerator(shift: 20));
        $this->assertSame(0x59DC920D23000DE, $little->next("\xDE", 1742951048483));
        $this->assertSame(0x59DC920D230ADDE, $little->next("\xDE\xAD", 1742951048483));
        $this->assertSame(0x59DC920D23DDE00, $little->next("\xDE\xAD\xBE", 1742951048483));
        $this->assertSame(0x59DC920D23EAFDE, $little->next("\xDE\xAF\xBE\xAD", 1742951048483));

        // random
        $random = new RandomSnowflake(new Mt19937(2525));
        $this->assertSame(0x59DCAD88D22D3E, $random->next(1742952849618));
        $this->assertSame(0x59DCAD88D2CFDC, $random->next(1742952849618));
        $this->assertSame(0x59DCAD88D27EA2, $random->next(1742952849618));
        $this->assertSame(0x59DCAD88D26E0A, $random->next(1742952849618));
        $this->assertSame(0x59DCAD88D24C90, $random->next(1742952849618));
    }
}