<?php

declare(strict_types=1);

use App\Framework\NanoId\NanoId;
use App\Framework\NanoId\NanoIdGenerator;
use App\Framework\Random\SecureRandomGenerator;

it('creates generator with default settings', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generate();

    expect($nanoId)->toBeInstanceOf(NanoId::class);
    expect($nanoId->getLength())->toBe(NanoId::DEFAULT_SIZE);
});

it('creates generator with custom settings', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator(), 10, 'ABC123');
    $nanoId = $generator->generate();

    expect($nanoId->getLength())->toBe(10);
    expect($nanoId->matchesAlphabet('ABC123'))->toBeTrue();
});

it('generates NanoId with custom size', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateWithSize(15);

    expect($nanoId->getLength())->toBe(15);
});

it('generates NanoId with custom alphabet', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateWithAlphabet('XYZ789');

    expect($nanoId->matchesAlphabet('XYZ789'))->toBeTrue();
});

it('generates custom NanoId with size and alphabet', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateCustom(8, 'ABCD');

    expect($nanoId->getLength())->toBe(8);
    expect($nanoId->matchesAlphabet('ABCD'))->toBeTrue();
});

it('generates safe NanoId', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateSafe();

    expect($nanoId->isSafe())->toBeTrue();
});

it('generates numeric NanoId', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateNumeric();

    expect($nanoId->isNumeric())->toBeTrue();
});

it('generates lowercase alphanumeric NanoId', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateLowercase();

    expect($nanoId->toString())->toMatch('/^[0-9a-z]+$/');
});

it('generates uppercase alphanumeric NanoId', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateUppercase();

    expect($nanoId->toString())->toMatch('/^[0-9A-Z]+$/');
});

it('generates NanoId for entity types', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());

    $userNanoId = $generator->generateForEntity('user');
    expect($userNanoId->toString())->toStartWith('usr_');

    $orderNanoId = $generator->generateForEntity('order');
    expect($orderNanoId->toString())->toStartWith('ord_');

    $customNanoId = $generator->generateForEntity('widget');
    expect($customNanoId->toString())->toStartWith('wid_');
});

it('generates time-prefixed NanoId', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $nanoId = $generator->generateTimePrefixed();

    expect($nanoId->toString())->toContain('_');

    // Check that prefix is a valid base36 timestamp
    $parts = explode('_', $nanoId->toString());
    expect($parts)->toHaveCount(2);

    $timestamp = base_convert($parts[0], 36, 10);
    expect($timestamp)->toBeNumeric();
    expect((int)$timestamp)->toBeLessThanOrEqual(time());
    expect((int)$timestamp)->toBeGreaterThan(time() - 10); // Within last 10 seconds
});

it('generates batch of unique NanoIds', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $batch = $generator->generateBatch(100);

    expect($batch)->toHaveCount(100);
    expect($batch[0])->toBeInstanceOf(NanoId::class);

    // Check uniqueness
    $values = array_map(fn ($id) => $id->toString(), $batch);
    $uniqueValues = array_unique($values);
    expect($uniqueValues)->toHaveCount(100);
});

it('validates NanoIds correctly', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());

    $validId = $generator->generate()->toString();
    expect($generator->isValid($validId))->toBeTrue();

    expect($generator->isValid(''))->toBeFalse();
    expect($generator->isValid(str_repeat('a', 256)))->toBeFalse();

    // Test with custom alphabet generator
    $customGenerator = new NanoIdGenerator(new SecureRandomGenerator(), 10, 'ABC');
    expect($customGenerator->isValid('ABCABC'))->toBeTrue();
    expect($customGenerator->isValid('XYZ123'))->toBeFalse();
});

it('creates generator using factory methods', function () {
    $randomGen = new SecureRandomGenerator();

    $defaultGenerator = NanoIdGenerator::create($randomGen);
    expect($defaultGenerator->generate())->toBeInstanceOf(NanoId::class);

    $safeGenerator = NanoIdGenerator::createSafe($randomGen);
    expect($safeGenerator->generate()->isSafe())->toBeTrue();

    $numericGenerator = NanoIdGenerator::createNumeric($randomGen);
    expect($numericGenerator->generate()->isNumeric())->toBeTrue();
});

it('throws exception for invalid default size', function () {
    $randomGen = new SecureRandomGenerator();
    expect(fn () => new NanoIdGenerator($randomGen, 0))->toThrow(InvalidArgumentException::class, 'Default size must be between 1 and 255');
    expect(fn () => new NanoIdGenerator($randomGen, 256))->toThrow(InvalidArgumentException::class, 'Default size must be between 1 and 255');
});

it('throws exception for empty default alphabet', function () {
    $randomGen = new SecureRandomGenerator();
    expect(fn () => new NanoIdGenerator($randomGen, 21, ''))->toThrow(InvalidArgumentException::class, 'Default alphabet cannot be empty');
});

it('throws exception for invalid batch count', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());

    expect(fn () => $generator->generateBatch(0))->toThrow(InvalidArgumentException::class, 'Count must be positive');
    expect(fn () => $generator->generateBatch(10001))->toThrow(InvalidArgumentException::class, 'Batch size cannot exceed 10000');
});

it('throws exception for invalid generation parameters', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());

    // Test invalid size
    expect(fn () => $generator->generateWithSize(0))->toThrow(InvalidArgumentException::class, 'Size must be between 1 and 255');
    expect(fn () => $generator->generateWithSize(256))->toThrow(InvalidArgumentException::class, 'Size must be between 1 and 255');

    // Test empty alphabet
    expect(fn () => $generator->generateWithAlphabet(''))->toThrow(InvalidArgumentException::class, 'Alphabet cannot be empty');

    // Test alphabet too long
    $longAlphabet = str_repeat('a', 256);
    expect(fn () => $generator->generateWithAlphabet($longAlphabet))->toThrow(InvalidArgumentException::class, 'Alphabet cannot exceed 255 characters');
});

it('generates different entity type prefixes correctly', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());

    $prefixes = [
        'user' => 'usr_',
        'order' => 'ord_',
        'product' => 'prd_',
        'session' => 'ses_',
        'token' => 'tok_',
        'transaction' => 'txn_',
        'invoice' => 'inv_',
        'customer' => 'cus_',
        'payment' => 'pay_',
        'subscription' => 'sub_',
    ];

    foreach ($prefixes as $entity => $expectedPrefix) {
        $nanoId = $generator->generateForEntity($entity);
        expect($nanoId->toString())->toStartWith($expectedPrefix);
    }
});

it('generates unique IDs across multiple calls', function () {
    $generator = new NanoIdGenerator(new SecureRandomGenerator());
    $ids = [];
    $count = 1000;

    for ($i = 0; $i < $count; $i++) {
        $ids[] = $generator->generate()->toString();
    }

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