<?php

if (!function_exists('base64url_encode')) {
    function base64url_encode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
}

if (!function_exists('jwt_encode')) {
    /**
     * Minimal HS256 JWT encoder.
     *
     * @param array  $payload
     * @param string $secret
     * @param string $alg Only HS256 supported here
     * @return string JWT string
     */
    function jwt_encode(array $payload, string $secret, string $alg = 'HS256'): string
    {
        $header = ['typ' => 'JWT', 'alg' => $alg];

        $segments = [];
        $segments[] = base64url_encode(json_encode($header, JSON_UNESCAPED_SLASHES));
        $segments[] = base64url_encode(json_encode($payload, JSON_UNESCAPED_SLASHES));

        $signingInput = implode('.', $segments);

        switch ($alg) {
            case 'HS256':
                $signature = hash_hmac('sha256', $signingInput, $secret, true);
                break;
            default:
                throw new \InvalidArgumentException('Unsupported JWT alg: ' . $alg);
        }

        $segments[] = base64url_encode($signature);
        return implode('.', $segments);
    }
}

if (!function_exists('base64url_decode')) {
    function base64url_decode(string $data): string
    {
        $padding = strlen($data) % 4;
        if ($padding > 0) {
            $data .= str_repeat('=', 4 - $padding);
        }
        return base64_decode(strtr($data, '-_', '+/'));
    }
}

if (!function_exists('jwt_decode')) {
    function jwt_decode(string $token, string $secret, string $alg = 'HS256'): ?array
    {
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return null;
        }

        [$header64, $payload64, $signature64] = $parts;
        $header = json_decode(base64url_decode($header64), true);
        $payload = json_decode(base64url_decode($payload64), true);
        $signature = base64url_decode($signature64);

        if (!is_array($header) || !is_array($payload)) {
            return null;
        }

        $signingInput = $header64 . '.' . $payload64;

        switch ($header['alg'] ?? $alg) {
            case 'HS256':
                $expected = hash_hmac('sha256', $signingInput, $secret, true);
                break;
            default:
                return null;
        }

        if (!hash_equals($expected, $signature)) {
            return null;
        }

        return $payload;
    }
}
