<?php

declare(strict_types=1);

namespace PpPhp\Core\Auth;

/**
 * Generated-app side of SSO (platform feature C1).
 *
 * Verifies a login-handoff token minted by the console's SsoIssuer, using the
 * PINNED issuer public key (the app ships only the public half — it can verify
 * but never forge). On success it hands the verified claims to an app-supplied
 * `$upsert` callback that maps the console identity onto a local user row (e.g.
 * upsert qa_users by sso_subject) and returns the local id.
 *
 * Pure given the callback → unit-testable. The app's SSO landing endpoint calls
 * handle() and, only on a non-null result, starts its own session for local_id.
 */
final class SsoConsumer
{
    /**
     * @param callable(array<string,mixed>):(int|string|null) $upsert  maps claims → local user id
     * @return array<string,mixed>|null  verified claims + ['local_id'=>…], or null on any failure
     */
    public static function handle(string $token, string $publicKeyBase64, callable $upsert): ?array
    {
        $pk = base64_decode(trim($publicKeyBase64), true);
        if ($pk === false || strlen($pk) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) {
            return null;
        }
        $claims = Sso::verify($token, $pk);
        if (!is_array($claims) || (string) ($claims['sub'] ?? '') === '') {
            return null;
        }
        $localId = $upsert($claims);
        if (empty($localId)) {
            return null;                                   // the app declined to provision this identity
        }
        return $claims + ['local_id' => $localId];
    }
}
