<?php

declare(strict_types=1);

namespace App\Framework\Ulid;

use App\Framework\DateTime\Clock;
use DateTimeImmutable;
use InvalidArgumentException;
use JsonSerializable;

/**
 * Objekt-Wrapper für ULIDs mit String-/JSON-API.
 */
final readonly class Ulid implements JsonSerializable
{
    private string $ulid;

    private StringConverter $converter;

    public function __construct(
        private Clock $clock,
        ?string $ulid = null,
        bool $isBase32 = false,
    ) {
        $gen = new UlidGenerator();
        $validator = new UlidValidator();
        $this->converter = new StringConverter();

        if ($ulid === null) {

            $this->ulid = $gen->generate($this->clock);

        } else {

            if ($isBase32) {
                $this->ulid = $this->converter->encodeBase32($ulid);

                return;
            }


            if (! $validator->isValid($ulid)) {
                throw new InvalidArgumentException("Ungültige ULID: $ulid");
            }
            $this->ulid = $ulid;
        }
    }

    public static function fromBase32(Clock $clock, string $ulid): self
    {
        return new self($clock, $ulid, true);
    }

    public function toBinary(): string
    {
        return $this->converter->decodeBase32($this->ulid);
    }

    public function __toString(): string
    {
        return $this->ulid;
    }

    public function jsonSerialize(): string
    {
        return $this->ulid;
    }

    public function getTimestampMs(): int
    {
        $parser = new ULIDParser(new ULIDValidator());

        return $parser->getTimestampMs($this->ulid);
    }

    public function getDateTime(): DateTimeImmutable
    {
        $parser = new ULIDParser(new ULIDValidator());

        return $parser->getDateTime($this->ulid, $this->clock);
    }

    public static function isValid(string $ulid): bool
    {
        return new ULIDValidator()->isValid($ulid);
    }

    public static function fromString(Clock $clock, string $ulid): self
    {
        return new self($clock, $ulid);
    }

    public function __debugInfo(): ?array
    {
        return [
            'ulid' => $this->ulid,
            'timestamp' => $this->getTimestampMs(),
            'clock' => $this->clock,
            'datetime' => $this->getDateTime(),
        ];
    }
}
