<?php
declare(strict_types=1);

namespace App\Framework\Ulid;

use InvalidArgumentException;

/**
 * A utility class for encoding and decoding Base32 strings using a custom alphabet.
 */
final readonly class StringConverter
{
    /**
     * Encodes a binary string into a Base32 encoded string using a custom alphabet.
     *
     * @param string $binary The binary string to be encoded.
     * @return string The Base32 encoded string.
     */
    public function encodeBase32(string $binary): string
    {
        $alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
        $bits = '';
        foreach (str_split($binary) as $byte) {
            $bits .= str_pad(decbin(ord($byte)), 8, '0', STR_PAD_LEFT);
        }

        $ulid = '';
        for ($i = 0; $i < 130; $i += 5) {
            $chunk = substr($bits, $i, 5);
            if (strlen($chunk) < 5) {
                $chunk = str_pad($chunk, 5, '0', STR_PAD_RIGHT);
            }
            $ulid .= $alphabet[bindec($chunk)];
        }
        return $ulid;
    }

    /**
     * Decodes a Base32-encoded string into its binary representation.
     *
     * @param string $base32 The Base32-encoded string to decode.
     * @return string The binary representation of the decoded string.
     * @throws InvalidArgumentException If the provided string contains invalid Base32 characters.
     */
    public function decodeBase32(string $base32): string
    {
        $alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
        $bits = '';

        // Jeden Base32-Charakter in 5 Bit umwandeln
        for ($i = 0, $len = strlen($base32); $i < $len; $i++) {
            $char = $base32[$i];
            $index = strpos($alphabet, $char);
            if ($index === false) {
                throw new InvalidArgumentException("Zeichen '$char' ist kein gültiges Base32-Zeichen.");
            }
            $bits .= str_pad(decbin($index), 5, '0', STR_PAD_LEFT);
        }

        // Bits in Bytes umwandeln (immer 8 Bit zusammenfassen)
        $binary = '';
        for ($i = 0, $bitsLen = strlen($bits); $i + 8 <= $bitsLen; $i += 8) {
            $byte = substr($bits, $i, 8);
            $binary .= chr(bindec($byte));
        }

        return $binary;
    }
}
