<?php

declare(strict_types=1);

namespace App\Framework\Ulid;

use App\Framework\DateTime\Clock;
use App\Framework\DateTime\SystemClock;

/**
 * ULID Generator - Universally Unique Lexicographically Sortable Identifier
 *
 * Generates 26-character, timestamp-based, sortable unique identifiers.
 * Drop-in replacement for deprecated uniqid() function.
 *
 * Usage:
 * - Production: new UlidGenerator() - uses SystemClock automatically
 * - Testing: new UlidGenerator($mockClock) - inject mock for deterministic tests
 */
final readonly class UlidGenerator
{
    public function __construct(
        private ?Clock $clock = null
    ) {}

    /**
     * Generate a new ULID
     *
     * @return string 26-character ULID (Base32 encoded)
     */
    public function generate(): string
    {
        $clock = $this->clock ?? new SystemClock();
        $stringConverter = new StringConverter();

        // Get timestamp in milliseconds (ULID uses millisecond precision)
        $timestamp = $clock->now()->getTimestamp() . $clock->now()->getMicrosecond();
        $time = (int)$timestamp / 1000;

        // Pack timestamp as 48-bit big-endian integer
        $timeBin = substr(pack('J', $time), 2, 6);

        // 80 bits of cryptographically secure randomness
        $random = random_bytes(10);

        // Combine timestamp and random bytes
        $bin = $timeBin . $random;

        return $stringConverter->encodeBase32($bin);
    }

    /**
     * Generate a ULID with a prefix
     *
     * Useful for namespacing/categorizing IDs (e.g., "user_01ARZ3NDEKTSV4...")
     *
     * @param string $prefix Prefix to prepend (without separator)
     * @return string Prefixed ULID
     */
    public function generateWithPrefix(string $prefix): string
    {
        return $prefix . '_' . $this->generate();
    }
}
