<?php

declare(strict_types=1);

namespace App\Core;

final class Jwt
{
    private static function b64url(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    private static function b64urlDecode(string $data): string|false
    {
        $remainder = strlen($data) % 4;
        if ($remainder) {
            $data .= str_repeat('=', 4 - $remainder);
        }
        return base64_decode(strtr($data, '-_', '+/'));
    }

    public static function encode(array $payload, ?int $ttlSeconds = null): string
    {
        $secret = Env::get('SECRET_KEY', Env::get('JWT_SECRET', '')) ?: '';
        if ($secret === '' || $secret === 'change-me') {
            throw new \RuntimeException('SECRET_KEY/JWT_SECRET não configurada ou insegura.');
        }
        $header = ['alg' => 'HS256', 'typ' => 'JWT'];
        $payload['iat'] = time();
        if ($ttlSeconds !== null) {
            $payload['exp'] = time() + $ttlSeconds;
        }
        $segments = [self::b64url(json_encode($header)), self::b64url(json_encode($payload))];
        $signature = hash_hmac('sha256', implode('.', $segments), $secret, true);
        $segments[] = self::b64url($signature);
        return implode('.', $segments);
    }

    public static function decode(?string $token): ?array
    {
        if (!$token) {
            return null;
        }
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return null;
        }
        [$h, $p, $s] = $parts;
        $secret = Env::get('SECRET_KEY', Env::get('JWT_SECRET', '')) ?: '';
        if ($secret === '' || $secret === 'change-me') {
            throw new \RuntimeException('SECRET_KEY/JWT_SECRET não configurada ou insegura.');
        }
        $expected = self::b64url(hash_hmac('sha256', $h . '.' . $p, $secret, true));
        if (!hash_equals($expected, $s)) {
            return null;
        }
        $payloadRaw = self::b64urlDecode($p);
        if ($payloadRaw === false) {
            return null;
        }
        $payload = json_decode($payloadRaw, true);
        if (!is_array($payload)) {
            return null;
        }
        if (isset($payload['exp']) && time() > (int) $payload['exp']) {
            return null;
        }
        return $payload;
    }
}
