<?php
/**
 * Normalize Google OpenID Connect userinfo / id_token claims.
 *
 * @package GoogleSignIn
 */

namespace GoogleSignIn;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class OidcProfile {

	/**
	 * Build a normalized profile from OIDC userinfo or id_token claims.
	 *
	 * Google OIDC returns `sub` (not legacy `id`). Both are accepted.
	 *
	 * @param array<string, mixed> $claims
	 * @return array{
	 *   id: string,
	 *   email: string,
	 *   email_verified: bool,
	 *   display_name: string,
	 *   first_name: string,
	 *   last_name: string,
	 *   avatar_url: string
	 * }|null
	 */
	public static function fromClaims( array $claims ): ?array {
		$subject = trim( (string) ( $claims['sub'] ?? $claims['id'] ?? '' ) );
		$email   = sanitize_email( (string) ( $claims['email'] ?? '' ) );
		if ( $subject === '' || $email === '' || ! is_email( $email ) ) {
			return null;
		}

		return [
			'id'             => $subject,
			'email'          => $email,
			'email_verified' => self::isTruthy( $claims['email_verified'] ?? false ),
			'display_name'   => (string) ( $claims['name'] ?? $claims['given_name'] ?? '' ),
			'first_name'     => (string) ( $claims['given_name'] ?? '' ),
			'last_name'      => (string) ( $claims['family_name'] ?? '' ),
			'avatar_url'     => (string) ( $claims['picture'] ?? '' ),
		];
	}

	/**
	 * Decode JWT payload (no signature verification — supplement to userinfo only).
	 *
	 * @return array<string, mixed>|null
	 */
	public static function decodeJwtPayload( string $jwt ): ?array {
		$parts = explode( '.', trim( $jwt ) );
		if ( count( $parts ) < 2 ) {
			return null;
		}
		$raw = self::base64UrlDecode( $parts[1] );
		if ( $raw === false || $raw === '' ) {
			return null;
		}
		$data = json_decode( $raw, true );
		return is_array( $data ) ? $data : null;
	}

	private static function base64UrlDecode( string $segment ): string|false {
		$segment = strtr( $segment, '-_', '+/' );
		$pad     = strlen( $segment ) % 4;
		if ( $pad > 0 ) {
			$segment .= str_repeat( '=', 4 - $pad );
		}
		return base64_decode( $segment, true );
	}

	private static function isTruthy( mixed $value ): bool {
		return $value === true || $value === 'true' || $value === 1 || $value === '1';
	}
}
