<?php
// ============================================================
// JWT Helper — Pure PHP HS256 Implementation (no Composer)
// ============================================================

class JWT {

    // ─── Encode ─────────────────────────────────────────────
    public static function encode(array $payload, string $secret, int $ttl): string {
        $payload['iat'] = time();
        $payload['exp'] = time() + $ttl;

        $header  = self::base64url(json_encode(['typ' => 'JWT', 'alg' => 'HS256']));
        $body    = self::base64url(json_encode($payload));
        $sig     = self::base64url(hash_hmac('sha256', "$header.$body", $secret, true));

        return "$header.$body.$sig";
    }

    // ─── Decode & Verify ─────────────────────────────────────
    public static function decode(string $token, string $secret): array {
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            throw new RuntimeException('invalid_token_structure');
        }

        [$headerB64, $bodyB64, $sigB64] = $parts;

        // Verify signature (constant-time comparison)
        $expectedSig = self::base64url(hash_hmac('sha256', "$headerB64.$bodyB64", $secret, true));
        if (!hash_equals($expectedSig, $sigB64)) {
            throw new RuntimeException('invalid_signature');
        }

        $payload = json_decode(self::base64urlDecode($bodyB64), true);
        if (!$payload) {
            throw new RuntimeException('invalid_payload');
        }

        if (isset($payload['exp']) && $payload['exp'] < time()) {
            throw new RuntimeException('token_expired');
        }

        return $payload;
    }

    // ─── Helpers ─────────────────────────────────────────────
    private static function base64url(string $data): string {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    private static function base64urlDecode(string $data): string {
        return base64_decode(strtr($data, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($data)) % 4));
    }
}
