<?php
declare(strict_types=1);

/**
 * Provider configuration store. All providers live in a single JSON-encoded
 * setting `oidc_providers`; `client_secret` is encrypted via Crypto::encrypt
 * before serialization, so the on-disk JSON only ever contains ciphertext.
 *
 * Provider record shape:
 *   id              — operator-defined slug, used in the redirect URL and
 *                     in users.external_id. Locked to [a-z0-9_-]+ at save time.
 *   kind            — 'keycloak' | 'google' | 'microsoft' | 'custom'.
 *                     Influences the discovery URL template and default scopes.
 *   label           — display name on the login button ("Sign in with …").
 *   discovery_url   — full URL to /.well-known/openid-configuration.
 *   client_id       — OAuth client identifier registered at the provider.
 *   client_secret_enc — Crypto::encrypt(...) of the client secret.
 *   scopes          — space-separated; default 'openid email profile'.
 *   enabled         — boolean.
 */
class OidcConfig
{
    private const ALLOWED_KINDS = ['keycloak', 'google', 'microsoft', 'custom'];

    public static function all(): array
    {
        $raw = Helper::getSetting('oidc_providers', '[]');
        $arr = json_decode((string)$raw, true);
        return is_array($arr) ? $arr : [];
    }

    public static function activeProviders(): array
    {
        return array_values(array_filter(self::all(), fn($p) => !empty($p['enabled'])));
    }

    public static function byId(string $id): ?array
    {
        foreach (self::all() as $p) {
            if (($p['id'] ?? '') === $id) return $p;
        }
        return null;
    }

    public static function save(array $providers): void
    {
        $clean = [];
        foreach ($providers as $p) {
            $id = (string)($p['id'] ?? '');
            if (!preg_match('/^[a-z0-9_-]+$/', $id)) continue;
            $kind = (string)($p['kind'] ?? 'custom');
            if (!in_array($kind, self::ALLOWED_KINDS, true)) $kind = 'custom';
            $clean[] = [
                'id'                => $id,
                'kind'              => $kind,
                'label'             => (string)($p['label'] ?? $id),
                'discovery_url'     => (string)($p['discovery_url'] ?? ''),
                'client_id'         => (string)($p['client_id'] ?? ''),
                'client_secret_enc' => (string)($p['client_secret_enc'] ?? ''),
                'scopes'            => (string)($p['scopes'] ?? 'openid email profile'),
                'enabled'           => !empty($p['enabled']),
            ];
        }
        Helper::setSetting('oidc_providers', json_encode($clean, JSON_UNESCAPED_SLASHES));
    }

    /**
     * Decrypt the bind/client secret. Returns '' on missing / decrypt failure.
     */
    public static function clientSecret(array $provider): string
    {
        $enc = (string)($provider['client_secret_enc'] ?? '');
        if ($enc === '') return '';
        try {
            return Crypto::decrypt($enc);
        } catch (\Throwable) {
            return '';
        }
    }

    /**
     * Suggested discovery URL given the provider kind and a per-kind input.
     * Used in the admin form to pre-fill discovery_url when the operator picks
     * a known kind.
     */
    public static function templateDiscoveryUrl(string $kind, string $input): string
    {
        $input = trim($input);
        return match ($kind) {
            'google'    => 'https://accounts.google.com/.well-known/openid-configuration',
            'microsoft' => $input !== ''
                ? 'https://login.microsoftonline.com/' . rawurlencode($input) . '/v2.0/.well-known/openid-configuration'
                : 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
            'keycloak'  => $input !== ''
                ? rtrim($input, '/') . '/.well-known/openid-configuration'
                : '',
            default     => $input,
        };
    }

    /** The redirect URI the operator must whitelist at the provider. */
    public static function redirectUri(): string
    {
        return Helper::baseUrl() . (defined('APP_BASE') ? APP_BASE : '') . '/auth/oidc/callback';
    }
}
