<?php

declare(strict_types=1);

namespace App\Support\Baselines;

use JsonException;

final class BaselineEvidenceResumeToken
{
    private const int VERSION = 1;

    /**
     * @param  array<string, mixed>  $state
     */
    public static function encode(array $state): string
    {
        $payload = [
            'v' => self::VERSION,
            'state' => $state,
        ];

        $json = json_encode($payload, JSON_THROW_ON_ERROR);

        return self::base64UrlEncode($json);
    }

    /**
     * @return array<string, mixed>|null
     */
    public static function decode(string $token): ?array
    {
        $token = trim($token);

        if ($token === '') {
            return null;
        }

        $json = self::base64UrlDecode($token);

        if ($json === null) {
            return null;
        }

        try {
            $decoded = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
        } catch (JsonException) {
            return null;
        }

        if (! is_array($decoded)) {
            return null;
        }

        $version = $decoded['v'] ?? null;
        $version = is_int($version) ? $version : (is_numeric($version) ? (int) $version : null);

        if ($version !== self::VERSION) {
            return null;
        }

        $state = $decoded['state'] ?? null;

        return is_array($state) ? $state : null;
    }

    private static function base64UrlEncode(string $value): string
    {
        return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
    }

    private static function base64UrlDecode(string $value): ?string
    {
        $padded = strtr($value, '-_', '+/');
        $padding = strlen($padded) % 4;

        if ($padding !== 0) {
            $padded .= str_repeat('=', 4 - $padding);
        }

        $decoded = base64_decode($padded, true);

        return is_string($decoded) ? $decoded : null;
    }
}
