<?php

namespace App\Services;

use Exception;
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use stdClass;

class AppleIdentityTokenService
{
    private const ISSUER = 'https://appleid.apple.com';
    private const KEYS_URL = 'https://appleid.apple.com/auth/keys';

    public function verify(string $identityToken, string $rawNonce): stdClass
    {
        $keys = Cache::remember('apple-sign-in-public-keys', now()->addHours(6), function (): array {
            $response = Http::timeout(10)->retry(2, 200)->get(self::KEYS_URL);
            if (!$response->successful() || !is_array($response->json())) {
                throw new Exception('Unable to retrieve Apple public keys.');
            }

            return $response->json();
        });

        try {
            $claims = JWT::decode($identityToken, JWK::parseKeySet($keys));
        } catch (Exception $exception) {
            throw new Exception('Invalid Apple identity token.', previous: $exception);
        }

        $audience = $claims->aud ?? null;
        $validAudience = is_array($audience)
            ? in_array(config('desxpert.apple_client_id'), $audience, true)
            : $audience === config('desxpert.apple_client_id');

        if (($claims->iss ?? null) !== self::ISSUER || !$validAudience) {
            throw new Exception('Invalid Apple identity token claims.');
        }

        $expectedNonce = hash('sha256', $rawNonce);
        if (!isset($claims->nonce) || !hash_equals($expectedNonce, (string) $claims->nonce)) {
            throw new Exception('Invalid Apple sign-in nonce.');
        }

        $emailVerified = filter_var($claims->email_verified ?? false, FILTER_VALIDATE_BOOLEAN);
        if (!isset($claims->sub) || !isset($claims->email) || !$emailVerified) {
            throw new Exception('Apple did not provide the required account information.');
        }

        return $claims;
    }
}
