<?php

declare(strict_types=1);

namespace App\Framework\Queue\ValueObjects;

use App\Framework\Ulid\Ulid;

/**
 * Value Object representing a unique Job identifier
 */
final readonly class JobId
{
    private function __construct(
        private string $value
    ) {
        if (empty($value)) {
            throw new \InvalidArgumentException('JobId cannot be empty');
        }

        if (! $this->isValidFormat($value)) {
            throw new \InvalidArgumentException('Invalid JobId format');
        }
    }

    /**
     * Create a new random JobId using ULID
     */
    public static function generate(): self
    {
        // Use simple uniqid for now to avoid dependency injection in Value Objects
        return new self(uniqid('job_', true));
    }

    /**
     * Create from existing string
     */
    public static function fromString(string $id): self
    {
        return new self($id);
    }

    /**
     * Create from ULID object
     */
    public static function fromUlid(Ulid $ulid): self
    {
        return new self($ulid->toString());
    }

    /**
     * Get string representation
     */
    public function toString(): string
    {
        return $this->value;
    }

    /**
     * Get value (alias for toString)
     */
    public function getValue(): string
    {
        return $this->value;
    }

    /**
     * Convert to ULID object
     */
    public function toUlid(): Ulid
    {
        return Ulid::fromString($this->value);
    }

    /**
     * Check if two JobIds are equal
     */
    public function equals(self $other): bool
    {
        return $this->value === $other->value;
    }

    /**
     * Check if JobId is valid ULID format
     */
    private function isValidFormat(string $value): bool
    {
        // Accept both ULID format (26 chars) and simple IDs (for testing)
        if (strlen($value) === 26) {
            // Valid ULID characters (Crockford's Base32)
            return preg_match('/^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/i', $value) === 1;
        }

        // Allow any non-empty string for now
        return strlen($value) > 0;
    }

    /**
     * Get creation timestamp from ULID
     */
    public function getTimestamp(): \DateTimeImmutable
    {
        $ulid = $this->toUlid();
        $timestamp = $ulid->getTimestamp();

        return \DateTimeImmutable::createFromFormat('U', (string)$timestamp);
    }

    /**
     * Create JobId for specific queue and timestamp
     */
    public static function generateForQueue(string $queueName): self
    {
        // Generate ULID with current timestamp
        return new self(Ulid::generate());
    }

    /**
     * Extract queue-specific prefix (first 10 chars of timestamp)
     */
    public function getTimePrefix(): string
    {
        return substr($this->value, 0, 10);
    }

    /**
     * Extract random suffix (last 16 chars)
     */
    public function getRandomSuffix(): string
    {
        return substr($this->value, 10, 16);
    }

    /**
     * Check if this job was created before another
     */
    public function isBefore(self $other): bool
    {
        return $this->value < $other->value;
    }

    /**
     * Check if this job was created after another
     */
    public function isAfter(self $other): bool
    {
        return $this->value > $other->value;
    }

    /**
     * String representation
     */
    public function __toString(): string
    {
        return $this->value;
    }

    /**
     * JSON serialization
     */
    public function jsonSerialize(): string
    {
        return $this->value;
    }
}
