<?php

declare(strict_types=1);

use App\Framework\LiveComponents\Services\UploadSessionIdGenerator;
use App\Framework\LiveComponents\ValueObjects\UploadSessionId;
use App\Framework\Random\RandomGenerator;

describe('UploadSessionIdGenerator Service', function () {
    it('generates valid session id', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);
        $sessionId = $generator->generate();

        expect($sessionId)->toBeInstanceOf(UploadSessionId::class);
        expect(strlen($sessionId->value))->toBe(32); // 16 bytes = 32 hex chars
    });

    it('generates hex-encoded session ids', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);
        $sessionId = $generator->generate();

        // Verify it's valid hex
        expect(ctype_xdigit($sessionId->value))->toBeTrue();
    });

    it('generates unique session ids', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);

        $id1 = $generator->generate();
        $id2 = $generator->generate();
        $id3 = $generator->generate();

        expect($id1->equals($id2))->toBeFalse();
        expect($id1->equals($id3))->toBeFalse();
        expect($id2->equals($id3))->toBeFalse();
    });

    it('generates 100 unique session ids', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);

        $sessionIds = [];
        for ($i = 0; $i < 100; $i++) {
            $sessionIds[] = $generator->generate()->value;
        }

        $uniqueIds = array_unique($sessionIds);
        expect(count($uniqueIds))->toBe(100);
    });

    it('requests 16 bytes from random generator', function () {
        $randomGen = new class implements RandomGenerator {
            public int $lastRequestedLength = 0;

            public function bytes(int $length): string
            {
                $this->lastRequestedLength = $length;
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);
        $generator->generate();

        expect($randomGen->lastRequestedLength)->toBe(16);
    });

    it('uses RandomGenerator bytes method', function () {
        $randomGen = new class implements RandomGenerator {
            public bool $bytesCalled = false;

            public function bytes(int $length): string
            {
                $this->bytesCalled = true;
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);
        $generator->generate();

        expect($randomGen->bytesCalled)->toBeTrue();
    });

    it('produces alphanumeric session ids', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);

        for ($i = 0; $i < 10; $i++) {
            $sessionId = $generator->generate();
            expect(ctype_alnum($sessionId->value))->toBeTrue();
        }
    });

    it('generates consistent length session ids', function () {
        $randomGen = new class implements RandomGenerator {
            public function bytes(int $length): string
            {
                return random_bytes($length);
            }

            public function int(int $min, int $max): int
            {
                return random_int($min, $max);
            }

            public function float(): float
            {
                return (float) random_int(0, PHP_INT_MAX) / PHP_INT_MAX;
            }
        };

        $generator = new UploadSessionIdGenerator($randomGen);

        $lengths = [];
        for ($i = 0; $i < 50; $i++) {
            $sessionId = $generator->generate();
            $lengths[] = strlen($sessionId->value);
        }

        $uniqueLengths = array_unique($lengths);
        expect(count($uniqueLengths))->toBe(1);
        expect($uniqueLengths[0])->toBe(32);
    });
});
