<?php

declare(strict_types=1);

namespace PpPhp\Core\Auth;

/**
 * Console-side SSO key holder + token issuer (platform feature C1).
 *
 * Owns a DEDICATED Ed25519 keypair at storage/sso/sso-key.json — separate from
 * the licence signing key, so SSO and licensing never share trust. Generates the
 * pair on first use (0600). Issues short-lived login-handoff tokens for the
 * current user; a generated app verifies them with the PUBLIC half only.
 *
 * SERVER-ONLY: this class loads the secret key, so the packager must keep it out
 * of client bundles (see PlatformPackager::$serverOnlyFiles). The generated app
 * pins only publicKeyBase64() and verifies via \PpPhp\Core\Auth\Sso::verify.
 */
final class SsoIssuer
{
    private string $path;

    public function __construct(?string $basePath = null)
    {
        $base = rtrim($basePath ?? (defined('BASE_PATH') ? (string) constant('BASE_PATH') : (string) getcwd()), '/');
        $this->path = $base . '/storage/sso/sso-key.json';
    }

    /** @return array{secret:string, public:string} raw key bytes */
    private function keys(): array
    {
        if (is_file($this->path)) {
            $d = json_decode((string) file_get_contents($this->path), true);
            if (is_array($d) && !empty($d['secret']) && !empty($d['public'])) {
                $sk = base64_decode((string) $d['secret'], true);
                $pk = base64_decode((string) $d['public'], true);
                if ($sk !== false && $pk !== false
                    && strlen($sk) === SODIUM_CRYPTO_SIGN_SECRETKEYBYTES
                    && strlen($pk) === SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) {
                    return ['secret' => $sk, 'public' => $pk];
                }
            }
        }
        $pair = sodium_crypto_sign_keypair();
        $sk = sodium_crypto_sign_secretkey($pair);
        $pk = sodium_crypto_sign_publickey($pair);
        $dir = dirname($this->path);
        if (!is_dir($dir)) {
            @mkdir($dir, 0700, true);
        }
        @file_put_contents($this->path, (string) json_encode([
            'secret' => base64_encode($sk),
            'public' => base64_encode($pk),
        ]));
        @chmod($this->path, 0600);
        return ['secret' => $sk, 'public' => $pk];
    }

    /** The public key, base64 — pin this into a generated app so it can verify tokens. */
    public function publicKeyBase64(): string
    {
        return base64_encode($this->keys()['public']);
    }

    /** Issue a login-handoff token for a console user record. */
    public function issueForUser(array $user, int $ttl = 300): string
    {
        return Sso::issue([
            'sub'   => (string) ($user['id'] ?? ''),
            'name'  => (string) ($user['fullname'] ?? $user['username'] ?? ''),
            'email' => (string) ($user['email'] ?? ''),
            'role'  => (string) ($user['role'] ?? 'member'),
        ], $this->keys()['secret'], $ttl);
    }

    /** Verify a token with this issuer's own key (used in tests / same-host checks). */
    public function verify(string $token): ?array
    {
        return Sso::verify($token, $this->keys()['public']);
    }
}
